mirror of
https://github.com/acmesh-official/acme.sh
synced 2025-11-07 16:01:51 +08:00
Compare commits
4071 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a2d556551 | ||
|
|
0f762d98a4 | ||
|
|
36752cb6a8 | ||
|
|
90b65c6618 | ||
|
|
7250a300df | ||
|
|
34bb00450d | ||
|
|
63dadd8983 | ||
|
|
67c990e8cf | ||
|
|
0292e20c86 | ||
|
|
9088c8741a | ||
|
|
faedea2120 | ||
|
|
e1a0f5706d | ||
|
|
a78a4e6716 | ||
|
|
6bbf927f57 | ||
|
|
4c32bc8e22 | ||
|
|
df671a77f6 | ||
|
|
dca9def42c | ||
|
|
edee7ea284 | ||
|
|
658d09ed84 | ||
|
|
c41de8f270 | ||
|
|
9d2ee2127d | ||
|
|
e49ece8793 | ||
|
|
188274277a | ||
|
|
c0cb3945f1 | ||
|
|
e07795e8f0 | ||
|
|
bda454fe9c | ||
|
|
856e77053e | ||
|
|
2ce145f359 | ||
|
|
7e7291ace9 | ||
|
|
61c853a3c1 | ||
|
|
4346139d65 | ||
|
|
1476f83ba7 | ||
|
|
c959d64099 | ||
|
|
e67d26caeb | ||
|
|
86c3fa0df0 | ||
|
|
75ae57e194 | ||
|
|
d42feae0af | ||
|
|
45971b8083 | ||
|
|
8e9bbd1bb3 | ||
|
|
ec10a3eab4 | ||
|
|
49deb4af24 | ||
|
|
10f171b6e4 | ||
|
|
735db1a12b | ||
|
|
b2f4cc2dc5 | ||
|
|
d43b587d17 | ||
|
|
6b07a955f2 | ||
|
|
e23c02575d | ||
|
|
b1bf634136 | ||
|
|
459faf4dfb | ||
|
|
8cdceb83b2 | ||
|
|
6aa1ec0802 | ||
|
|
0727f7054b | ||
|
|
37cc611e3f | ||
|
|
c39e6c4423 | ||
|
|
1566656af3 | ||
|
|
737eba57bd | ||
|
|
d32cedd7dc | ||
|
|
2b6aa26703 | ||
|
|
95f1336060 | ||
|
|
f46ee93597 | ||
|
|
56d799f449 | ||
|
|
6251652c93 | ||
|
|
6a2c9a0dc1 | ||
|
|
4dd709b543 | ||
|
|
aa9f5b8c4a | ||
|
|
f485f3fdb5 | ||
|
|
c6a0ec64cb | ||
|
|
e0b179e5f3 | ||
|
|
dac7a3d272 | ||
|
|
beed123fb0 | ||
|
|
267e582827 | ||
|
|
69c02cae76 | ||
|
|
4f386663e7 | ||
|
|
eaae0547f2 | ||
|
|
b203f2abaa | ||
|
|
5490a2f3ba | ||
|
|
ba44235471 | ||
|
|
be556f9e36 | ||
|
|
5e5ba11601 | ||
|
|
e384df30fa | ||
|
|
4635dacf7f | ||
|
|
18e4d270d9 | ||
|
|
3bcb91f6ae | ||
|
|
5e574a355d | ||
|
|
640c7c5fa3 | ||
|
|
eb6395a62c | ||
|
|
2b2845aa07 | ||
|
|
54d8c66f3e | ||
|
|
95bbf1b190 | ||
|
|
ee2dab51f3 | ||
|
|
f63409eed9 | ||
|
|
ad8940ad73 | ||
|
|
b8bfb5a56c | ||
|
|
3f58823430 | ||
|
|
e488220bfc | ||
|
|
e6e0771496 | ||
|
|
fe77d43fa0 | ||
|
|
737a7a2db2 | ||
|
|
6ae8d10132 | ||
|
|
7d249b6d3b | ||
|
|
a532b82771 | ||
|
|
e8756482aa | ||
|
|
35d6da785b | ||
|
|
dbdcbd4b9e | ||
|
|
a2b6f49c5c | ||
|
|
a31ed4a723 | ||
|
|
b2fc84c98e | ||
|
|
927369b06d | ||
|
|
401fd37e35 | ||
|
|
759cdf10c5 | ||
|
|
6e7ce1eec1 | ||
|
|
2a65955e88 | ||
|
|
5b0d6a1375 | ||
|
|
0510da0853 | ||
|
|
365d22d076 | ||
|
|
1760169ef9 | ||
|
|
32ea224933 | ||
|
|
38a067e203 | ||
|
|
ab6f1b6df7 | ||
|
|
b36802edff | ||
|
|
1d2af0f291 | ||
|
|
40e8c5e2b0 | ||
|
|
e11d0d37ee | ||
|
|
ea4266538a | ||
|
|
aa7bf9169f | ||
|
|
a95e83ab6e | ||
|
|
86daaf4bf2 | ||
|
|
d5b4f02932 | ||
|
|
15deec6c53 | ||
|
|
17b1875151 | ||
|
|
bfda8f0b8a | ||
|
|
7bb0ff986b | ||
|
|
f3cfef4021 | ||
|
|
3184c3c21b | ||
|
|
9264737985 | ||
|
|
c9b353a689 | ||
|
|
fda6502f33 | ||
|
|
0fdac82b93 | ||
|
|
6b3d6d5211 | ||
|
|
cb89ee39f5 | ||
|
|
be827be742 | ||
|
|
8fcecd59a0 | ||
|
|
83b49b23e4 | ||
|
|
f5ee618986 | ||
|
|
8339b88180 | ||
|
|
f2958818c8 | ||
|
|
608547c62c | ||
|
|
20f604948f | ||
|
|
f72a4f966d | ||
|
|
bd00db4292 | ||
|
|
68debc474a | ||
|
|
6a7f993a9a | ||
|
|
ae25931b37 | ||
|
|
84fe6654cc | ||
|
|
d2d023cca7 | ||
|
|
64908e0080 | ||
|
|
d4e1899747 | ||
|
|
7f9b8d68ac | ||
|
|
5b1e849bde | ||
|
|
fba6de76b1 | ||
|
|
8419b42e83 | ||
|
|
5f38c15b1f | ||
|
|
c3b72baa8e | ||
|
|
16d0416f22 | ||
|
|
b9aa4f4478 | ||
|
|
8d3ad3a8c1 | ||
|
|
5a689ce897 | ||
|
|
35e22703af | ||
|
|
41a2d0e06c | ||
|
|
4d95e35c06 | ||
|
|
4e553f34ba | ||
|
|
b910726c43 | ||
|
|
64e3cab6ab | ||
|
|
f3196396a2 | ||
|
|
148336929d | ||
|
|
2f3ec3a77f | ||
|
|
8d7a487013 | ||
|
|
622464ff5e | ||
|
|
b45a44e405 | ||
|
|
a196958bd6 | ||
|
|
f101418658 | ||
|
|
aa05a1e81d | ||
|
|
384bc62f25 | ||
|
|
46e62f1a9a | ||
|
|
c5c2014081 | ||
|
|
c668c603cc | ||
|
|
8f3b7c179e | ||
|
|
ea18c47011 | ||
|
|
ced7110a78 | ||
|
|
92f13eb8bf | ||
|
|
1312ef7e50 | ||
|
|
e992979113 | ||
|
|
d317b49940 | ||
|
|
6be53468c5 | ||
|
|
046094bdcb | ||
|
|
1064c270d9 | ||
|
|
2447fccf1e | ||
|
|
c43c711f72 | ||
|
|
f354e6de69 | ||
|
|
12615c46f8 | ||
|
|
b335840f97 | ||
|
|
c5efec678e | ||
|
|
83cb89e4f7 | ||
|
|
6bdf689d0f | ||
|
|
e164362069 | ||
|
|
72e3f33f28 | ||
|
|
ccfd907914 | ||
|
|
5a44e63cad | ||
|
|
d96cca3822 | ||
|
|
06580bf0e4 | ||
|
|
b21bd64764 | ||
|
|
6b97dc6734 | ||
|
|
4e3f328a02 | ||
|
|
8380ca2fdd | ||
|
|
ec678bc6d2 | ||
|
|
2b5e2d4760 | ||
|
|
89abad7980 | ||
|
|
d84da5bdbf | ||
|
|
5cc1d9521c | ||
|
|
a199fc6113 | ||
|
|
655e34b166 | ||
|
|
5ea3a02d6a | ||
|
|
15c68c9594 | ||
|
|
08438608d1 | ||
|
|
2da94e0fdf | ||
|
|
63165764dc | ||
|
|
103810ce20 | ||
|
|
f59356b96b | ||
|
|
4a8511f680 | ||
|
|
cd6698c688 | ||
|
|
c7ca9d7e36 | ||
|
|
d70b759cb9 | ||
|
|
ae3dda0f8f | ||
|
|
e229ba5945 | ||
|
|
dcc50093bb | ||
|
|
0831690f79 | ||
|
|
98ef51514f | ||
|
|
ac9993394c | ||
|
|
849c3fd9c9 | ||
|
|
da58fcbfce | ||
|
|
fa3cd9736f | ||
|
|
a0c5d17539 | ||
|
|
224cd04673 | ||
|
|
52243d0870 | ||
|
|
d519873fa4 | ||
|
|
a76dcd4ba1 | ||
|
|
2d07185300 | ||
|
|
518e1df257 | ||
|
|
e0def66959 | ||
|
|
772d970074 | ||
|
|
ba7d85145a | ||
|
|
fb73dceab0 | ||
|
|
13fd83e0ba | ||
|
|
719ba75fcc | ||
|
|
13ab98440c | ||
|
|
1752004301 | ||
|
|
536a5f7cff | ||
|
|
a69aece23a | ||
|
|
77d3815baa | ||
|
|
e225e17386 | ||
|
|
3290208749 | ||
|
|
3106187aac | ||
|
|
eae490b5b1 | ||
|
|
bcce77508a | ||
|
|
e9bdf02cfc | ||
|
|
77f659c9b9 | ||
|
|
c66e157a14 | ||
|
|
2c927277e2 | ||
|
|
29fe1c86da | ||
|
|
1ae9c48370 | ||
|
|
078a8b40e9 | ||
|
|
9daeae1695 | ||
|
|
014e016058 | ||
|
|
f41f93af3a | ||
|
|
51539521b1 | ||
|
|
522dec34a5 | ||
|
|
ea6d76cce6 | ||
|
|
7c7d61f61e | ||
|
|
184dde92a2 | ||
|
|
7e43c794fd | ||
|
|
c7285967d6 | ||
|
|
41f4baadb9 | ||
|
|
20082ec9fb | ||
|
|
8dae8c52c0 | ||
|
|
068076c0d5 | ||
|
|
c0ae44a41b | ||
|
|
593e8e1f63 | ||
|
|
707cf35f0a | ||
|
|
30f11d0e16 | ||
|
|
53d6ab6c23 | ||
|
|
280e44304a | ||
|
|
79fac4466e | ||
|
|
0e9f09e582 | ||
|
|
25d0fdf8ff | ||
|
|
1d089d4541 | ||
|
|
1c78663378 | ||
|
|
7666022840 | ||
|
|
946c8b498a | ||
|
|
406ca66c8d | ||
|
|
ed971df93a | ||
|
|
74c054b2a5 | ||
|
|
017a10189c | ||
|
|
f1c361855e | ||
|
|
3d72df4666 | ||
|
|
6f732a9957 | ||
|
|
447bf77dfe | ||
|
|
6621ef6a0b | ||
|
|
e327c8758e | ||
|
|
8a08de5691 | ||
|
|
3d7375be8b | ||
|
|
da754e9a71 | ||
|
|
cc9ec806b2 | ||
|
|
72cf037c0d | ||
|
|
8ae08b29e4 | ||
|
|
4967fa020f | ||
|
|
54f2640ef2 | ||
|
|
56246592c7 | ||
|
|
1ff5d71e12 | ||
|
|
67c42c5911 | ||
|
|
d0b514890a | ||
|
|
c0285fbc15 | ||
|
|
a438c841e1 | ||
|
|
19d7c2b336 | ||
|
|
afb6c70909 | ||
|
|
d9af496b13 | ||
|
|
a55cf40b1b | ||
|
|
b19008d1b8 | ||
|
|
6f88c81616 | ||
|
|
43cb230f19 | ||
|
|
fd6a59202d | ||
|
|
e353f66eaa | ||
|
|
9293bcfb1c | ||
|
|
d154118600 | ||
|
|
c2273d2c8e | ||
|
|
495ba01d8e | ||
|
|
f627a02886 | ||
|
|
5f9daa6640 | ||
|
|
9edda556de | ||
|
|
3891a52aeb | ||
|
|
1e5e3353f3 | ||
|
|
7aa4b8247c | ||
|
|
37339ddafc | ||
|
|
dcb51683c5 | ||
|
|
74a4a788b1 | ||
|
|
7909273a21 | ||
|
|
130e8dbd40 | ||
|
|
40e2ebed95 | ||
|
|
bf9b33acec | ||
|
|
7710a33b6c | ||
|
|
af3ea2d4fd | ||
|
|
52a3255936 | ||
|
|
5ab9ca1c0d | ||
|
|
461f602992 | ||
|
|
46180435cc | ||
|
|
7f2699c6da | ||
|
|
aede5c486b | ||
|
|
fb2407386f | ||
|
|
aa59c46c4c | ||
|
|
07afc4953a | ||
|
|
c5557fc488 | ||
|
|
8c14150536 | ||
|
|
c127903127 | ||
|
|
0881cf1379 | ||
|
|
d0a16b0ec0 | ||
|
|
290beb90a7 | ||
|
|
0a4ef17135 | ||
|
|
e0c32ce700 | ||
|
|
e65144a105 | ||
|
|
ae40445dba | ||
|
|
25a8240d12 | ||
|
|
1a4a180e8c | ||
|
|
aea10a3b93 | ||
|
|
f855862ade | ||
|
|
91d37c7875 | ||
|
|
5707b93110 | ||
|
|
81b2d0732f | ||
|
|
ec0538d251 | ||
|
|
eb0c629fad | ||
|
|
509d3f6d30 | ||
|
|
c576af7c6f | ||
|
|
f3682f0e8e | ||
|
|
1c58913eeb | ||
|
|
cfbc294832 | ||
|
|
64ad8b1dac | ||
|
|
fb079f9e50 | ||
|
|
39b09f8f87 | ||
|
|
53d26e5c5c | ||
|
|
e21f3e6c73 | ||
|
|
edd46eb3d1 | ||
|
|
e71238571a | ||
|
|
2867ec509e | ||
|
|
d853a9ebbe | ||
|
|
e0d2fa98f3 | ||
|
|
cc90f83463 | ||
|
|
9ea1238e1b | ||
|
|
1530abbd1a | ||
|
|
3bfcd18a03 | ||
|
|
6b7db22981 | ||
|
|
8adb8a6986 | ||
|
|
37e3e2f9c2 | ||
|
|
3c7be32ef5 | ||
|
|
051775b9b4 | ||
|
|
e0d5b91388 | ||
|
|
4dd2027428 | ||
|
|
42ab98b830 | ||
|
|
2b2bce6457 | ||
|
|
69ee816541 | ||
|
|
2cbf3f7e15 | ||
|
|
923eece3f5 | ||
|
|
d4fb313ff0 | ||
|
|
7dce465c06 | ||
|
|
5a30f5c00e | ||
|
|
fd406af962 | ||
|
|
9e5ae30372 | ||
|
|
0fe3538331 | ||
|
|
b0f5ad75ae | ||
|
|
a290f63a15 | ||
|
|
d078ce794e | ||
|
|
06f51a5c34 | ||
|
|
db96778064 | ||
|
|
d8918ea156 | ||
|
|
3503474bb8 | ||
|
|
eb1606b086 | ||
|
|
1330a092fa | ||
|
|
d3c74cfb45 | ||
|
|
d044545520 | ||
|
|
d1cdc1c6a0 | ||
|
|
dc8d91ea39 | ||
|
|
ae5a6d330d | ||
|
|
fe0bee21b0 | ||
|
|
c090c19bfe | ||
|
|
8fbec785e8 | ||
|
|
06fb3d9476 | ||
|
|
fe3e8a7bb6 | ||
|
|
ce2ff25edd | ||
|
|
65a1b892e3 | ||
|
|
e272fde95e | ||
|
|
d48bff0e20 | ||
|
|
6e61c34f0f | ||
|
|
2d9506eb54 | ||
|
|
b1988c7b67 | ||
|
|
fb5d72c29b | ||
|
|
ac148ce0e9 | ||
|
|
016dca654e | ||
|
|
6502bdecbe | ||
|
|
91a739af6e | ||
|
|
e232565971 | ||
|
|
472488ebe8 | ||
|
|
8de3698b23 | ||
|
|
a694b46914 | ||
|
|
c384ed960c | ||
|
|
2386d2e299 | ||
|
|
cc7e1a72c1 | ||
|
|
5cc0fa7c98 | ||
|
|
4ce848ab51 | ||
|
|
2e34e11b02 | ||
|
|
8eda5f36fb | ||
|
|
3dbe5d872b | ||
|
|
96a95ba9fe | ||
|
|
f594ed659e | ||
|
|
5c4bfbbd95 | ||
|
|
8733635638 | ||
|
|
928aa74e89 | ||
|
|
8fdfe673e8 | ||
|
|
494a6e6090 | ||
|
|
89bb7e6b0e | ||
|
|
52cfb9a041 | ||
|
|
3817ddef41 | ||
|
|
0f494c9dd6 | ||
|
|
3ff97ecf45 | ||
|
|
a10c0b516b | ||
|
|
5eb1469dbf | ||
|
|
9a90fe3794 | ||
|
|
1917c4b04a | ||
|
|
2b01d4a203 | ||
|
|
c5100219d1 | ||
|
|
a730a08161 | ||
|
|
4bb8e3a121 | ||
|
|
eacc00f786 | ||
|
|
17f5e557ed | ||
|
|
c33e5bc40f | ||
|
|
06d1a98ad2 | ||
|
|
afe6f4030e | ||
|
|
6e49c4ffe0 | ||
|
|
28d9f00610 | ||
|
|
8f688e5e13 | ||
|
|
5a182eddbf | ||
|
|
4b615cb3a9 | ||
|
|
b36247a091 | ||
|
|
6e77756d6a | ||
|
|
585c0c3818 | ||
|
|
6ff75f9a9f | ||
|
|
906ef43c00 | ||
|
|
4528957235 | ||
|
|
9d448a42a7 | ||
|
|
979e10f9d5 | ||
|
|
31f65b89bb | ||
|
|
86639dbc02 | ||
|
|
bf8c33703c | ||
|
|
ffe7ef4764 | ||
|
|
30dae70e2b | ||
|
|
557a747d55 | ||
|
|
fe273b3829 | ||
|
|
2439bb30e8 | ||
|
|
e48b6bd22d | ||
|
|
1de9ffacb0 | ||
|
|
93fd6170a3 | ||
|
|
b7c3e6099c | ||
|
|
d8163e9835 | ||
|
|
12b1916599 | ||
|
|
9d8cdb5976 | ||
|
|
29a7c1938a | ||
|
|
8636d3139e | ||
|
|
987571ce91 | ||
|
|
6f4c5fcc87 | ||
|
|
a7ca010d4e | ||
|
|
a836842a7e | ||
|
|
f8c11a324a | ||
|
|
052c9be111 | ||
|
|
854e520528 | ||
|
|
05737b85eb | ||
|
|
c8a2308739 | ||
|
|
ed46a078f9 | ||
|
|
4f7c2bf8c3 | ||
|
|
0e341726d2 | ||
|
|
a4f9746d3a | ||
|
|
556c546b2e | ||
|
|
aa479948f9 | ||
|
|
fa3cee9d58 | ||
|
|
0021fb8a33 | ||
|
|
c8c727e6c6 | ||
|
|
b8494ab3cc | ||
|
|
2eda03f5de | ||
|
|
3a38358946 | ||
|
|
e7fc697e57 | ||
|
|
6c9845b9f3 | ||
|
|
9e146a8a5a | ||
|
|
433d9bfb02 | ||
|
|
94917e315e | ||
|
|
ced6852735 | ||
|
|
8dc55f417d | ||
|
|
3d338bba3c | ||
|
|
631398f700 | ||
|
|
7984d8cdfb | ||
|
|
783a6110ef | ||
|
|
5d4d53c3a1 | ||
|
|
3014955ece | ||
|
|
0481f20c6b | ||
|
|
76309601eb | ||
|
|
84dd864886 | ||
|
|
7924e01b15 | ||
|
|
dadc70630b | ||
|
|
effc37a702 | ||
|
|
deac3fc918 | ||
|
|
e6dea4c92c | ||
|
|
075e992fa0 | ||
|
|
565ca81b30 | ||
|
|
58c4eaaf86 | ||
|
|
77e8008752 | ||
|
|
2ba10fcbc7 | ||
|
|
4956a58026 | ||
|
|
92332fc385 | ||
|
|
9366f4b40e | ||
|
|
f49e8ec5ad | ||
|
|
cd33647087 | ||
|
|
71ebcac7f2 | ||
|
|
f06aee21eb | ||
|
|
5fbbc17376 | ||
|
|
a9d8830106 | ||
|
|
d21e6235ad | ||
|
|
289f79bbb0 | ||
|
|
768e9f4c09 | ||
|
|
62c776d90c | ||
|
|
464022bea2 | ||
|
|
61549b4a74 | ||
|
|
18df3dc07a | ||
|
|
3725724c54 | ||
|
|
500a005aac | ||
|
|
584cc6de2e | ||
|
|
2e5a6e21cf | ||
|
|
b79f63db78 | ||
|
|
7ed7a57d92 | ||
|
|
1eaf7c89b7 | ||
|
|
1fff8dd306 | ||
|
|
c3a3d02bea | ||
|
|
a9261970dd | ||
|
|
b7a3fe05a4 | ||
|
|
ab6b9006b7 | ||
|
|
d9a8b057c3 | ||
|
|
2ec6215b1c | ||
|
|
c59a8c9644 | ||
|
|
10de4b6b7b | ||
|
|
0be214e79e | ||
|
|
d6083c68fd | ||
|
|
22f7ac22d5 | ||
|
|
a00046f9b2 | ||
|
|
8a24275ba9 | ||
|
|
ca841252bd | ||
|
|
54195b16ad | ||
|
|
cb90167c76 | ||
|
|
ac4ae85a4a | ||
|
|
48b2a271cc | ||
|
|
596807055e | ||
|
|
cee20c4eb9 | ||
|
|
15fb47cb3d | ||
|
|
2635dfef96 | ||
|
|
7d7789ae96 | ||
|
|
cc69285420 | ||
|
|
99d3a283ef | ||
|
|
9b532584d6 | ||
|
|
7576eb38d9 | ||
|
|
8440d013f8 | ||
|
|
174c87a192 | ||
|
|
32b62d6d4f | ||
|
|
a0c2d312e9 | ||
|
|
5127a9ae3c | ||
|
|
b5653a1c06 | ||
|
|
671bd1022e | ||
|
|
94bba4ac9c | ||
|
|
fe1136aa95 | ||
|
|
8950ffcc5e | ||
|
|
2bc627970e | ||
|
|
44615c6fa2 | ||
|
|
00f55ea0bc | ||
|
|
be43cebf7d | ||
|
|
f38317d01f | ||
|
|
da839aae66 | ||
|
|
768e00ff1a | ||
|
|
40631f465e | ||
|
|
f665c73bb1 | ||
|
|
be067466fe | ||
|
|
3c309df6dd | ||
|
|
b7e6d98647 | ||
|
|
48942de75e | ||
|
|
fbcbc10174 | ||
|
|
342b48105f | ||
|
|
5f3e7f02cc | ||
|
|
bfccf29ccf | ||
|
|
1e3bb1f02b | ||
|
|
0dcf6771e7 | ||
|
|
062503c523 | ||
|
|
c3d7f2f170 | ||
|
|
faf6c16717 | ||
|
|
3d79d78134 | ||
|
|
35cab4ee73 | ||
|
|
a0edb8f2ad | ||
|
|
5cfe5e312b | ||
|
|
13f6ec04d5 | ||
|
|
2edc4a79b9 | ||
|
|
4661185719 | ||
|
|
895da5cbf0 | ||
|
|
ef9147512b | ||
|
|
abe05456f7 | ||
|
|
f9dfd3b348 | ||
|
|
9449501537 | ||
|
|
f3b5d5ab7b | ||
|
|
8ee5726e0c | ||
|
|
effa7fd57d | ||
|
|
6c039d2ad0 | ||
|
|
2dd8527566 | ||
|
|
fe811ce32e | ||
|
|
9fee0805c4 | ||
|
|
1987c32761 | ||
|
|
0ed2659698 | ||
|
|
9878856dfe | ||
|
|
ed01fd4edf | ||
|
|
9474933070 | ||
|
|
f3987b453c | ||
|
|
dcb4cb3a1e | ||
|
|
198b840059 | ||
|
|
42583cf3bb | ||
|
|
2e15371d61 | ||
|
|
339ff8ca77 | ||
|
|
268eaddad8 | ||
|
|
bb3cc1130b | ||
|
|
64ae3280e1 | ||
|
|
95235d69c2 | ||
|
|
d639c7be39 | ||
|
|
d7cafe25ff | ||
|
|
996f53373e | ||
|
|
6f55370ad4 | ||
|
|
fd511966a7 | ||
|
|
c4ddddd434 | ||
|
|
83a4db3b31 | ||
|
|
e35ef75949 | ||
|
|
d9dec6fe6b | ||
|
|
69bdbaed41 | ||
|
|
30f359e642 | ||
|
|
29d0a1714e | ||
|
|
fcb97f802f | ||
|
|
9ad05e640d | ||
|
|
449f00f960 | ||
|
|
8e64329d05 | ||
|
|
4284777556 | ||
|
|
81c496d96c | ||
|
|
65c06da275 | ||
|
|
5d0657c49a | ||
|
|
f6f6550bfb | ||
|
|
e01fb50359 | ||
|
|
28ce1c1249 | ||
|
|
7db592d27a | ||
|
|
b8e5c0d898 | ||
|
|
067c1771d0 | ||
|
|
349429b76e | ||
|
|
cc8f2afce9 | ||
|
|
2e97b20f94 | ||
|
|
1a163243ec | ||
|
|
75660e6f21 | ||
|
|
199ca77c2a | ||
|
|
1e2d2abbdf | ||
|
|
11b980f574 | ||
|
|
3274f9f155 | ||
|
|
f90f8824bb | ||
|
|
c7116d40ca | ||
|
|
6ef66399f8 | ||
|
|
2b8561f27d | ||
|
|
b20d8f195b | ||
|
|
6cf0eb9e1d | ||
|
|
c349e9aabe | ||
|
|
6ee38ceaba | ||
|
|
c60613fbcb | ||
|
|
bcc1b7b48a | ||
|
|
b19cb0805c | ||
|
|
7dfc5a78ba | ||
|
|
a077132d82 | ||
|
|
8ed6be6307 | ||
|
|
c490dd1563 | ||
|
|
d866b3df1f | ||
|
|
97f3fb4496 | ||
|
|
7530266330 | ||
|
|
6a53f356d2 | ||
|
|
075dc1e4e9 | ||
|
|
97b87d4ce4 | ||
|
|
e203e98375 | ||
|
|
9fcd104065 | ||
|
|
178e0ba87c | ||
|
|
cc40110d7e | ||
|
|
d58fb2bbc0 | ||
|
|
812333e9ae | ||
|
|
92bbdce435 | ||
|
|
bc62d49fc9 | ||
|
|
fe54d5b8ae | ||
|
|
7cc30c268b | ||
|
|
df60a2248a | ||
|
|
aa85d0ffeb | ||
|
|
1db963361c | ||
|
|
8a55b20284 | ||
|
|
beec349bc5 | ||
|
|
b025ed6057 | ||
|
|
27a54bcbaa | ||
|
|
6b20993d2a | ||
|
|
9ab16bdbb3 | ||
|
|
23088bc897 | ||
|
|
054a62de60 | ||
|
|
5aff548794 | ||
|
|
ff8fe7e018 | ||
|
|
c6617ebc9f | ||
|
|
15fa0c264f | ||
|
|
25468f55ff | ||
|
|
2340c55d76 | ||
|
|
13c1f4ab19 | ||
|
|
a160b798ca | ||
|
|
71f00a9efd | ||
|
|
967096f01c | ||
|
|
7616e94fd3 | ||
|
|
27ec69fb97 | ||
|
|
182d150eaa | ||
|
|
098ef976f7 | ||
|
|
ea724e343b | ||
|
|
85736d697c | ||
|
|
576a146ed2 | ||
|
|
69c5291e52 | ||
|
|
4875ef045a | ||
|
|
369cfc2413 | ||
|
|
491842ea34 | ||
|
|
9801876a2f | ||
|
|
9c28a04c65 | ||
|
|
596a1764ef | ||
|
|
8e09e1b248 | ||
|
|
d5674c85d7 | ||
|
|
ed6649b1d3 | ||
|
|
02baa778c5 | ||
|
|
5fd0e5add2 | ||
|
|
23eccb2f20 | ||
|
|
3c523fb824 | ||
|
|
5dbfc2786d | ||
|
|
c61495df52 | ||
|
|
6ad5ea1696 | ||
|
|
39fa27a2dc | ||
|
|
348bae53fe | ||
|
|
6bc00fc5e5 | ||
|
|
54c0f015f9 | ||
|
|
ea3e6dae93 | ||
|
|
80e52c73b0 | ||
|
|
5c893f0f39 | ||
|
|
e05dc99006 | ||
|
|
f864416e39 | ||
|
|
472dbd641c | ||
|
|
05141b4f52 | ||
|
|
784b914e07 | ||
|
|
4db7f6f59c | ||
|
|
edbf8509e1 | ||
|
|
a017fbadd3 | ||
|
|
201f4b7e4a | ||
|
|
c9ff536e24 | ||
|
|
238efb02c6 | ||
|
|
f7e12b629f | ||
|
|
7cbca7fc1e | ||
|
|
be7b87cda3 | ||
|
|
07979a13fb | ||
|
|
9073c4554f | ||
|
|
8694e0ad19 | ||
|
|
60fe987a5f | ||
|
|
86256162de | ||
|
|
db24ca3dc1 | ||
|
|
5e3aa2db1d | ||
|
|
b74a501fac | ||
|
|
490a7d4a78 | ||
|
|
b147195189 | ||
|
|
b561666d80 | ||
|
|
a0b5305e3e | ||
|
|
e7a6c17260 | ||
|
|
c8ee9e6447 | ||
|
|
f2d350002e | ||
|
|
fabd26f85b | ||
|
|
17dcf7d2e5 | ||
|
|
6f62995c96 | ||
|
|
f405f4bbc4 | ||
|
|
98124de362 | ||
|
|
1e4ea90021 | ||
|
|
053f4a9a2e | ||
|
|
2c7d2230b3 | ||
|
|
040e0d8387 | ||
|
|
b5c382f929 | ||
|
|
0c9c1ae673 | ||
|
|
da0e0bdaec | ||
|
|
1f5b6a6a35 | ||
|
|
d25b2890be | ||
|
|
d73438a397 | ||
|
|
e91538a554 | ||
|
|
8a3b514372 | ||
|
|
7eca955b79 | ||
|
|
4242354c03 | ||
|
|
8728389c88 | ||
|
|
6651801b3f | ||
|
|
9190ce3701 | ||
|
|
90e2064d72 | ||
|
|
943d419f98 | ||
|
|
551316bcb6 | ||
|
|
9dd5089940 | ||
|
|
06e7ebbdeb | ||
|
|
a83b16e12a | ||
|
|
91a8b97cf4 | ||
|
|
41754c92c3 | ||
|
|
e544995b83 | ||
|
|
45cf5c4c0f | ||
|
|
900eedfc2e | ||
|
|
faaa7bfa3a | ||
|
|
70366a98bd | ||
|
|
e88180b4d5 | ||
|
|
b639683ac1 | ||
|
|
918c8f9295 | ||
|
|
c2214cd4b5 | ||
|
|
7d7e5bac12 | ||
|
|
185b558561 | ||
|
|
4632035581 | ||
|
|
d7f81dff23 | ||
|
|
c849738c6f | ||
|
|
3cd7a2e6d6 | ||
|
|
0d4904f05d | ||
|
|
0b539a5977 | ||
|
|
395fdc9d61 | ||
|
|
3b3d7eff3c | ||
|
|
763c05313b | ||
|
|
9f80df3fcb | ||
|
|
f170ee9e59 | ||
|
|
339218508d | ||
|
|
f0c710b245 | ||
|
|
e66337a1db | ||
|
|
e087bccd33 | ||
|
|
8017774bf3 | ||
|
|
5f4d08ada5 | ||
|
|
1ad450d753 | ||
|
|
f1692b3436 | ||
|
|
2a9c56d9e3 | ||
|
|
39a5688464 | ||
|
|
e4e6173eff | ||
|
|
cf7334eb7d | ||
|
|
fdb96e91f1 | ||
|
|
844c5027ea | ||
|
|
8d0e485120 | ||
|
|
281ee1a853 | ||
|
|
a0bf29e600 | ||
|
|
d66c430e46 | ||
|
|
f511a52705 | ||
|
|
a674e410e0 | ||
|
|
fc63445c80 | ||
|
|
0c15655574 | ||
|
|
328b6d1cc6 | ||
|
|
b67d663a38 | ||
|
|
dd6c5c9eea | ||
|
|
2e87e64bd1 | ||
|
|
08c210d833 | ||
|
|
a9403013df | ||
|
|
05477c1a03 | ||
|
|
fcb6198a82 | ||
|
|
410d0bc125 | ||
|
|
abc62b9348 | ||
|
|
6fbf33c8f4 | ||
|
|
0ceb750dc7 | ||
|
|
a48c22d14f | ||
|
|
1521199e44 | ||
|
|
2910be82a4 | ||
|
|
07fdb087dc | ||
|
|
58150f5dcd | ||
|
|
e7a6ff39f9 | ||
|
|
b086afb272 | ||
|
|
7decf76883 | ||
|
|
d81369d63a | ||
|
|
c0fbe8237b | ||
|
|
58923b2846 | ||
|
|
d9f9477a52 | ||
|
|
966c744992 | ||
|
|
ddc91ce7c3 | ||
|
|
7e5107d90f | ||
|
|
c1668c9bdb | ||
|
|
7ddc2ccf1a | ||
|
|
3cd85fb395 | ||
|
|
b805ea9bf6 | ||
|
|
edd76f595a | ||
|
|
c131b63852 | ||
|
|
a70f377388 | ||
|
|
50f76446a9 | ||
|
|
15ce3ec41a | ||
|
|
b7b01999d9 | ||
|
|
956114fc42 | ||
|
|
bb3a986859 | ||
|
|
50fefc3bb0 | ||
|
|
c5eea2e7c5 | ||
|
|
19555a98ed | ||
|
|
9021f006f0 | ||
|
|
2d5f14388e | ||
|
|
ab47bf6451 | ||
|
|
d8bd45c2bd | ||
|
|
de692d3dcc | ||
|
|
4adb525513 | ||
|
|
72235a5f72 | ||
|
|
b6508cccec | ||
|
|
d9e7cf659e | ||
|
|
cf500cd817 | ||
|
|
f1338aca84 | ||
|
|
284de4ac5d | ||
|
|
19c4345162 | ||
|
|
d5d38b3331 | ||
|
|
bd04638d27 | ||
|
|
2f0d0e7c7a | ||
|
|
0b531e9fbc | ||
|
|
e3ebd582ec | ||
|
|
95ef046d0a | ||
|
|
21fd46d66b | ||
|
|
b3a801df11 | ||
|
|
a6d22e3b22 | ||
|
|
0415c050a5 | ||
|
|
014396cf11 | ||
|
|
70488f9c56 | ||
|
|
0052ab7148 | ||
|
|
f725040dd5 | ||
|
|
f131863642 | ||
|
|
8791047005 | ||
|
|
836a293f16 | ||
|
|
1177cc3f29 | ||
|
|
269847d19d | ||
|
|
df22f68088 | ||
|
|
d83d8552b8 | ||
|
|
365aa69afd | ||
|
|
578c338d40 | ||
|
|
389518e1b8 | ||
|
|
d42ff227f1 | ||
|
|
737e9e48ca | ||
|
|
f96d91cb6c | ||
|
|
85503655ab | ||
|
|
8d811760a9 | ||
|
|
4e0de22375 | ||
|
|
7cfbf100eb | ||
|
|
edbe026b49 | ||
|
|
1e967eceef | ||
|
|
7d20db93d3 | ||
|
|
fb05a42d2e | ||
|
|
9490ae1440 | ||
|
|
e932be0fb3 | ||
|
|
70b49980cb | ||
|
|
5e6ee5fd48 | ||
|
|
a2d872a9f0 | ||
|
|
e8bcde31b7 | ||
|
|
40cda9220a | ||
|
|
a494683bc8 | ||
|
|
2ba6a85eca | ||
|
|
0e58158a59 | ||
|
|
af740592c9 | ||
|
|
c9452c9f31 | ||
|
|
494a1603e4 | ||
|
|
5207e111e1 | ||
|
|
06995fb080 | ||
|
|
1f5cafc2d1 | ||
|
|
f190de39a6 | ||
|
|
272ab746a6 | ||
|
|
fce0bf6e59 | ||
|
|
5957786b2c | ||
|
|
4f3f4e23e4 | ||
|
|
5f5096e1d4 | ||
|
|
4b35aef728 | ||
|
|
6a0ed51f5e | ||
|
|
67360e93b8 | ||
|
|
f18f4c69f2 | ||
|
|
41435578d2 | ||
|
|
7f33ae3bee | ||
|
|
645135bf56 | ||
|
|
eb9005ad74 | ||
|
|
14089f8c6a | ||
|
|
49094120d9 | ||
|
|
fe4111a9f5 | ||
|
|
61613bee98 | ||
|
|
3ce967d8e5 | ||
|
|
aad9afad59 | ||
|
|
8cbbd022fc | ||
|
|
9b23cd6d19 | ||
|
|
d94f241d3c | ||
|
|
bb7c11adf1 | ||
|
|
5c295254bf | ||
|
|
236e8cc95c | ||
|
|
421973e0d9 | ||
|
|
e2a5af1cf7 | ||
|
|
65aa7b1084 | ||
|
|
f8c8330258 | ||
|
|
f5411ac9ab | ||
|
|
f31debc09c | ||
|
|
6654d7a919 | ||
|
|
44743b5f6f | ||
|
|
f80276584f | ||
|
|
cda41debfc | ||
|
|
2c50d01c26 | ||
|
|
01ebb6576d | ||
|
|
aaca0b6f76 | ||
|
|
84e1f3649f | ||
|
|
0ab2cfaf8b | ||
|
|
0545d6f083 | ||
|
|
d804228956 | ||
|
|
f7d70df2ba | ||
|
|
4daef52991 | ||
|
|
a329547682 | ||
|
|
f02af8d481 | ||
|
|
dd6c067832 | ||
|
|
dbc435506c | ||
|
|
f00e289014 | ||
|
|
1dffaba266 | ||
|
|
958a2f4274 | ||
|
|
21718a69d3 | ||
|
|
c650ae0e19 | ||
|
|
9ed435d04a | ||
|
|
fed6a0c24e | ||
|
|
44b9a8e7ed | ||
|
|
c16757b03a | ||
|
|
5d0dde5c15 | ||
|
|
f60356e8c7 | ||
|
|
cdf8f78962 | ||
|
|
4539d236df | ||
|
|
8718ac0c4b | ||
|
|
94787d537a | ||
|
|
bb8cff967e | ||
|
|
eca57beec1 | ||
|
|
58b4eb04f9 | ||
|
|
a9d46297c4 | ||
|
|
e9edecf34a | ||
|
|
71a5f0e84e | ||
|
|
fb22ee94d9 | ||
|
|
f03904ebce | ||
|
|
6cc9f49d97 | ||
|
|
f5f0680ec7 | ||
|
|
60e04b9065 | ||
|
|
025da92450 | ||
|
|
5de4aa091b | ||
|
|
1fe8235a85 | ||
|
|
0ab14399ae | ||
|
|
15dded712c | ||
|
|
8837f7e6e8 | ||
|
|
e8defd821a | ||
|
|
5d6effeff5 | ||
|
|
427c278012 | ||
|
|
a78a09f594 | ||
|
|
59fd48cfe2 | ||
|
|
cc78ab4855 | ||
|
|
063562261e | ||
|
|
70619dd0b7 | ||
|
|
63031fb278 | ||
|
|
7f924a56b3 | ||
|
|
114f2a1465 | ||
|
|
5d2777634a | ||
|
|
2d5b4a0003 | ||
|
|
adfa1704e2 | ||
|
|
ab3fd6be8f | ||
|
|
47702d075e | ||
|
|
341f000b9c | ||
|
|
0deea53931 | ||
|
|
8b3d792bec | ||
|
|
b82c48b66f | ||
|
|
fa91516dce | ||
|
|
4954b44d8e | ||
|
|
d132e51ac7 | ||
|
|
243b6ae985 | ||
|
|
8780ba3626 | ||
|
|
694194be2f | ||
|
|
c7f61f8b80 | ||
|
|
3a7c7fe4e8 | ||
|
|
668967a719 | ||
|
|
d15c14ab93 | ||
|
|
52b81608a1 | ||
|
|
048f754d83 | ||
|
|
d5ef3a3f8c | ||
|
|
e768e285ce | ||
|
|
a102d775b2 | ||
|
|
65e82b03ad | ||
|
|
80a636bd14 | ||
|
|
a4c57ee363 | ||
|
|
94bf54e7e0 | ||
|
|
b18ce5ade0 | ||
|
|
99793bb2c4 | ||
|
|
093936e594 | ||
|
|
036a37e351 | ||
|
|
d904df57ca | ||
|
|
d507979ec1 | ||
|
|
9bbcfead67 | ||
|
|
81036894c0 | ||
|
|
9044adecb5 | ||
|
|
9190fdd42c | ||
|
|
eab35605e4 | ||
|
|
28b65a7e7b | ||
|
|
5d1d2308b4 | ||
|
|
cf5952f508 | ||
|
|
3b0d7bc4ad | ||
|
|
22f8ab110e | ||
|
|
8f2d085d28 | ||
|
|
c3d7f5b28b | ||
|
|
45e6000619 | ||
|
|
7cd00a6760 | ||
|
|
cecc53fed3 | ||
|
|
58c2c70146 | ||
|
|
ad9f488df6 | ||
|
|
b19799bc72 | ||
|
|
1209b9b86e | ||
|
|
da957a3caf | ||
|
|
1bfd0f0149 | ||
|
|
3ff48b8559 | ||
|
|
6ba1eda96f | ||
|
|
dddfe07867 | ||
|
|
054f67eeb8 | ||
|
|
65c59c8c30 | ||
|
|
24925a1739 | ||
|
|
c49b40ee95 | ||
|
|
c06db30a65 | ||
|
|
0ed6fef49b | ||
|
|
9bad11ec79 | ||
|
|
74cdcde449 | ||
|
|
1613461504 | ||
|
|
0043f3558c | ||
|
|
a9c4b8dd1a | ||
|
|
5d00edc896 | ||
|
|
3bad815982 | ||
|
|
08cc7587ab | ||
|
|
458c0db3a8 | ||
|
|
a995333081 | ||
|
|
8eb608a839 | ||
|
|
1041c9f9fc | ||
|
|
1564742b76 | ||
|
|
b887fd153d | ||
|
|
d083674fb1 | ||
|
|
ed7a945261 | ||
|
|
ef5ffa939f | ||
|
|
d842ccb287 | ||
|
|
233893f122 | ||
|
|
2febdfc363 | ||
|
|
2c971a2598 | ||
|
|
b4c3c20e5e | ||
|
|
b6fbb012ad | ||
|
|
7f4db5a731 | ||
|
|
4dfdfa0b7d | ||
|
|
dbf659c575 | ||
|
|
cd8e04471c | ||
|
|
93de1e4903 | ||
|
|
2df43c9e2b | ||
|
|
5ace44493a | ||
|
|
a57ba3d81c | ||
|
|
6298112531 | ||
|
|
25afca55f6 | ||
|
|
52a16c917f | ||
|
|
eef9a60037 | ||
|
|
e158b5ccf6 | ||
|
|
f03d7efb5e | ||
|
|
da7b1fb014 | ||
|
|
62378d063e | ||
|
|
5fac282ee0 | ||
|
|
6eff873a07 | ||
|
|
1fb306c9d3 | ||
|
|
c064b3896a | ||
|
|
8400d1e60e | ||
|
|
5530e74382 | ||
|
|
47a38a8977 | ||
|
|
47883a94a6 | ||
|
|
ca19bbd366 | ||
|
|
7595808d26 | ||
|
|
37d22a144a | ||
|
|
dc697a6862 | ||
|
|
5398bac533 | ||
|
|
9984a168cb | ||
|
|
286f3713b0 | ||
|
|
ff9be30f86 | ||
|
|
34cebe8c0c | ||
|
|
200cd5972a | ||
|
|
808d1af578 | ||
|
|
d0995665a3 | ||
|
|
6132af8ecb | ||
|
|
2e9c4914a8 | ||
|
|
bf0d513e5b | ||
|
|
2be435ff32 | ||
|
|
20ba820253 | ||
|
|
44fd332965 | ||
|
|
0453d656d6 | ||
|
|
cb7e38577d | ||
|
|
4fa59ea04e | ||
|
|
bc2ed602e7 | ||
|
|
a1c4d159dd | ||
|
|
598f29b78e | ||
|
|
f61f2d6e5e | ||
|
|
f38df4df11 | ||
|
|
1e34ccbe2e | ||
|
|
2a8746f6b0 | ||
|
|
554e083f3d | ||
|
|
5d881a8b0f | ||
|
|
3d81641139 | ||
|
|
c25b4ba099 | ||
|
|
fd64c20807 | ||
|
|
80f1034dd6 | ||
|
|
15b841da06 | ||
|
|
902c08e9c9 | ||
|
|
ea652c023e | ||
|
|
3c79bb77db | ||
|
|
d8dbb85946 | ||
|
|
20702d26ec | ||
|
|
7d7e9501fa | ||
|
|
efef76d9cf | ||
|
|
e1e1ee31f0 | ||
|
|
142ca58d38 | ||
|
|
3b01bf7bda | ||
|
|
30416f54d1 | ||
|
|
f21ef0d2e9 | ||
|
|
39ced21a6f | ||
|
|
0f24417cb3 | ||
|
|
f84a87f2a2 | ||
|
|
dada57e5c4 | ||
|
|
d437d6fde9 | ||
|
|
69b11575e3 | ||
|
|
72e1a1b2e9 | ||
|
|
8ba573d196 | ||
|
|
4593231049 | ||
|
|
de9eac760b | ||
|
|
12ad8d52ae | ||
|
|
22f9a3b467 | ||
|
|
b64f0ba83f | ||
|
|
f73a494407 | ||
|
|
46ee74ed16 | ||
|
|
806b746fc0 | ||
|
|
cc820e97c6 | ||
|
|
283b04df73 | ||
|
|
6420d1239f | ||
|
|
04771e5a4a | ||
|
|
3d9608faa0 | ||
|
|
b7b4ae4262 | ||
|
|
b73b078705 | ||
|
|
887fa8649b | ||
|
|
f6172d7273 | ||
|
|
c70681712d | ||
|
|
80ca6de531 | ||
|
|
c6c395cd0f | ||
|
|
4831064623 | ||
|
|
d4660a23c0 | ||
|
|
e184a1b9e6 | ||
|
|
f8662c9bc2 | ||
|
|
463df9e4ba | ||
|
|
c768581829 | ||
|
|
754f7a7891 | ||
|
|
21450a08c2 | ||
|
|
c355b25bb1 | ||
|
|
1fe3d80838 | ||
|
|
930e16b64a | ||
|
|
2077a70d03 | ||
|
|
cbdb8bd9b9 | ||
|
|
5dcb417676 | ||
|
|
71bc993e3d | ||
|
|
c2812896f8 | ||
|
|
d43227ede4 | ||
|
|
8554ae38ed | ||
|
|
da656caf1e | ||
|
|
51fc853228 | ||
|
|
7a30cb9de7 | ||
|
|
1b475cf9f3 | ||
|
|
719b690451 | ||
|
|
3cdc523dec | ||
|
|
eb49127b9e | ||
|
|
d07172a528 | ||
|
|
79637097ba | ||
|
|
1259341095 | ||
|
|
5d3bc95ac5 | ||
|
|
de25232a73 | ||
|
|
95769de464 | ||
|
|
52a168b961 | ||
|
|
b3b00b6700 | ||
|
|
8e8cda132c | ||
|
|
6459ccb185 | ||
|
|
548f83c3ad | ||
|
|
555e0de9e4 | ||
|
|
cc4bce283f | ||
|
|
8189a34d14 | ||
|
|
5d88ad554f | ||
|
|
2cc50a2b65 | ||
|
|
33670a5bd0 | ||
|
|
64f8a222cb | ||
|
|
d9a9695fe0 | ||
|
|
8e6c4e1aca | ||
|
|
490fbfc13e | ||
|
|
4b45973361 | ||
|
|
4c27e08e3d | ||
|
|
94eb80597b | ||
|
|
d610eb15d8 | ||
|
|
4f53cd1704 | ||
|
|
d795fac37a | ||
|
|
ce6b71a58b | ||
|
|
09f74a9af8 | ||
|
|
552a49a680 | ||
|
|
97741398fb | ||
|
|
f8b225e70e | ||
|
|
57b4eda014 | ||
|
|
fc3a181779 | ||
|
|
9541ea6a9f | ||
|
|
f716f6060e | ||
|
|
dc0cca8c83 | ||
|
|
4f303de00c | ||
|
|
05aa26e619 | ||
|
|
1c4b831922 | ||
|
|
6613ae57b0 | ||
|
|
b6552aff75 | ||
|
|
3c98fae4f2 | ||
|
|
2028e4c8ad | ||
|
|
5c7feba77b | ||
|
|
23f2677052 | ||
|
|
6b67511748 | ||
|
|
36e0feea43 | ||
|
|
69392f67e8 | ||
|
|
e7d130cc11 | ||
|
|
6e3ba3ca45 | ||
|
|
0f54cf83f4 | ||
|
|
fb209cdfde | ||
|
|
431c53efcf | ||
|
|
79ad0ff56b | ||
|
|
d4dad58b0d | ||
|
|
baff032e3b | ||
|
|
26309f51e3 | ||
|
|
f8f53a6bd9 | ||
|
|
ac3667c765 | ||
|
|
96180e7555 | ||
|
|
024619676b | ||
|
|
5aa0f547cf | ||
|
|
b1ce6ffcc7 | ||
|
|
f01936ca4f | ||
|
|
70fdb1042f | ||
|
|
58cfc0d0cf | ||
|
|
4eff3b6a24 | ||
|
|
e5f69f0815 | ||
|
|
ef7b51beb7 | ||
|
|
8494ac8f3d | ||
|
|
8dea519235 | ||
|
|
0712e98904 | ||
|
|
c7ccddbcb9 | ||
|
|
efd3e8067b | ||
|
|
c6f7b7f35f | ||
|
|
f3dd1603db | ||
|
|
be7688a4df | ||
|
|
8e2f11389d | ||
|
|
346454c21b | ||
|
|
c822870cf8 | ||
|
|
9666cf680e | ||
|
|
a88622c1be | ||
|
|
c3fbc36ce7 | ||
|
|
f174d7dd39 | ||
|
|
38a8721a91 | ||
|
|
8aedf26a87 | ||
|
|
b2ff9240ac | ||
|
|
7a3c61b744 | ||
|
|
8dd1df71cc | ||
|
|
b59b0f0386 | ||
|
|
f59f484c01 | ||
|
|
a44ea0ddf0 | ||
|
|
2214507db0 | ||
|
|
a138425417 | ||
|
|
99c47dd50a | ||
|
|
594b83e7a6 | ||
|
|
76d0ef0851 | ||
|
|
549ebbb462 | ||
|
|
341656ddb9 | ||
|
|
5a3c3b4876 | ||
|
|
375b8dceb7 | ||
|
|
f37546e173 | ||
|
|
239d53426a | ||
|
|
3ccac629bc | ||
|
|
e8e6feeb0f | ||
|
|
c22705a593 | ||
|
|
ec7889dfa8 | ||
|
|
563d59526f | ||
|
|
0ffd5de6fc | ||
|
|
5014f83b86 | ||
|
|
953a9b1768 | ||
|
|
c641b61b26 | ||
|
|
d87e507865 | ||
|
|
ec1f9841b2 | ||
|
|
84b0f29d87 | ||
|
|
b23e05dbc5 | ||
|
|
37978b4fe5 | ||
|
|
ef15e55947 | ||
|
|
c282dd086f | ||
|
|
aac9f089d9 | ||
|
|
adce8f52e8 | ||
|
|
20b64c8900 | ||
|
|
66d781a226 | ||
|
|
867ec010ab | ||
|
|
ce0c6da9fa | ||
|
|
e22c5ea800 | ||
|
|
16bfb1727f | ||
|
|
f1f14040b8 | ||
|
|
9cb328966c | ||
|
|
0c76890572 | ||
|
|
18fc42e63b | ||
|
|
fc8d9df516 | ||
|
|
afdf8a78c0 | ||
|
|
0b3ae1f972 | ||
|
|
6a5ee72722 | ||
|
|
c6ec8bc0d9 | ||
|
|
51cfd996eb | ||
|
|
6d0e4bed4b | ||
|
|
05247dc4a4 | ||
|
|
43011f3bfa | ||
|
|
38854bd876 | ||
|
|
a9726bd52f | ||
|
|
4216c9e8f7 | ||
|
|
a8d670fc0d | ||
|
|
cbacc779fc | ||
|
|
896778cead | ||
|
|
04b0c62bf9 | ||
|
|
63a779baa8 | ||
|
|
2d1a776db7 | ||
|
|
3d2df3ba93 | ||
|
|
eb6238781d | ||
|
|
6140a3c26b | ||
|
|
6eaf2d67b7 | ||
|
|
35b34c43ed | ||
|
|
f67a9d2de1 | ||
|
|
05acf28e0d | ||
|
|
5698bec621 | ||
|
|
3ec495225f | ||
|
|
fee9baca89 | ||
|
|
bec26ce754 | ||
|
|
343d7df57c | ||
|
|
dca6a4bbd5 | ||
|
|
a32b95544b | ||
|
|
2422e0b481 | ||
|
|
3441bd0e7c | ||
|
|
05ced9fbc4 | ||
|
|
b7c3df455e | ||
|
|
d7affad059 | ||
|
|
34c3da9117 | ||
|
|
a22d3b2390 | ||
|
|
7c09bdc6e0 | ||
|
|
bc291141b1 | ||
|
|
c1b089d1c3 | ||
|
|
1271f97b66 | ||
|
|
582c77805c | ||
|
|
9eb5f65b8f | ||
|
|
00f01a9889 | ||
|
|
671edc33e1 | ||
|
|
1253357a39 | ||
|
|
6df31eb7f5 | ||
|
|
9299a83b17 | ||
|
|
a6614abd24 | ||
|
|
4c9d99040c | ||
|
|
6ef8adc863 | ||
|
|
a00300f88a | ||
|
|
274393ac64 | ||
|
|
1339b9422d | ||
|
|
ed9e196bf6 | ||
|
|
bba5376a36 | ||
|
|
df3575217a | ||
|
|
c504506455 | ||
|
|
2a28772312 | ||
|
|
d04c6dd3ac | ||
|
|
e48daffad9 | ||
|
|
58642286c9 | ||
|
|
6d62ae226a | ||
|
|
14f6f9ec94 | ||
|
|
e10f447b5b | ||
|
|
1d1f61613c | ||
|
|
b85c1a8861 | ||
|
|
430956d304 | ||
|
|
c0449a3ed2 | ||
|
|
18ad01533b | ||
|
|
288049dc95 | ||
|
|
573c8f3b13 | ||
|
|
e85d7a7be5 | ||
|
|
7015215f26 | ||
|
|
ffa5472b31 | ||
|
|
e00f0b4cf1 | ||
|
|
dc5c220e8f | ||
|
|
933d49b0b0 | ||
|
|
9af85f5a7e | ||
|
|
7ec52145e8 | ||
|
|
aa6112482d | ||
|
|
d035cdcff9 | ||
|
|
7ad3ddef2a | ||
|
|
38e08bb91f | ||
|
|
252a21e2ae | ||
|
|
ba7db3edda | ||
|
|
f64b061a28 | ||
|
|
f323ced4ca | ||
|
|
c06ec7c6ba | ||
|
|
835f9aad91 | ||
|
|
a4ec9f8b44 | ||
|
|
47c33d0344 | ||
|
|
f500c7abcb | ||
|
|
95dd7b5323 | ||
|
|
65c950e1a4 | ||
|
|
e484f32b1a | ||
|
|
bc396e7a90 | ||
|
|
ce182f43db | ||
|
|
9382d52d55 | ||
|
|
dd156d0689 | ||
|
|
ac9f6e3a41 | ||
|
|
1e7534b9d7 | ||
|
|
72d800ed10 | ||
|
|
54143ae6d4 | ||
|
|
8ef5daa807 | ||
|
|
fa47ea4196 | ||
|
|
683592fa86 | ||
|
|
477a04760c | ||
|
|
b4a62bfa30 | ||
|
|
10eec7d48c | ||
|
|
b8cc10ab5d | ||
|
|
be0df07dfb | ||
|
|
5244097e2d | ||
|
|
1ba4ab2bd1 | ||
|
|
2e855f8983 | ||
|
|
51b4a9e350 | ||
|
|
237d28cf83 | ||
|
|
5723fd112f | ||
|
|
73b89c554e | ||
|
|
950d024a11 | ||
|
|
815a3be48b | ||
|
|
bfa6e52470 | ||
|
|
ec654d2355 | ||
|
|
dfb4883c93 | ||
|
|
4bf1f579f5 | ||
|
|
b9994e52eb | ||
|
|
80d63dbb7c | ||
|
|
6b817d4563 | ||
|
|
1081d98bf9 | ||
|
|
91d82da497 | ||
|
|
0ca46774ac | ||
|
|
f0d6d46766 | ||
|
|
4a81205e04 | ||
|
|
0ac37981cb | ||
|
|
400c31d031 | ||
|
|
54b38086e5 | ||
|
|
e0deca33d0 | ||
|
|
0b2b8b960b | ||
|
|
9b173dcd71 | ||
|
|
a3361806ab | ||
|
|
5bdfdfefbe | ||
|
|
ee38cccad8 | ||
|
|
f82ff90f06 | ||
|
|
9826b8ae69 | ||
|
|
c7849a43e1 | ||
|
|
874bd093cb | ||
|
|
2cbc04e04d | ||
|
|
143eac092c | ||
|
|
d74dfb1f5c | ||
|
|
d42cf6daeb | ||
|
|
75191e7187 | ||
|
|
b9b2cd278b | ||
|
|
55dea4ee9d | ||
|
|
41c951811e | ||
|
|
72e7eb6777 | ||
|
|
9a733a57e7 | ||
|
|
45e8bb03e4 | ||
|
|
54e189616c | ||
|
|
c25947d544 | ||
|
|
80af3d6ada | ||
|
|
93d29a9733 | ||
|
|
ccc2142b45 | ||
|
|
28c153a0a2 | ||
|
|
b8e6287774 | ||
|
|
8d393ff137 | ||
|
|
5c09788ec4 | ||
|
|
5e970cdca4 | ||
|
|
28cadc5e06 | ||
|
|
3cdfa4051d | ||
|
|
28a9df669d | ||
|
|
5f94474330 | ||
|
|
42497028c4 | ||
|
|
57b16e3ac2 | ||
|
|
bd9af86de1 | ||
|
|
2ce9fb9760 | ||
|
|
ae66c6f0b4 | ||
|
|
fe5f34231b | ||
|
|
d694ee8651 | ||
|
|
89c0b67b8c | ||
|
|
0e9fbf3c5a | ||
|
|
ec40807c54 | ||
|
|
971a85a6f8 | ||
|
|
8e36695bc4 | ||
|
|
2e2e056198 | ||
|
|
e0d4115ed7 | ||
|
|
6ff3f5d1ff | ||
|
|
a2738e8599 | ||
|
|
468edfa6cc | ||
|
|
c83f2f98bd | ||
|
|
06f860c8ea | ||
|
|
24e574ee09 | ||
|
|
803b67af9f | ||
|
|
f803c6c0bf | ||
|
|
254feecf21 | ||
|
|
c6b6855131 | ||
|
|
9bc45563ee | ||
|
|
882ac74a0c | ||
|
|
d883a870e1 | ||
|
|
f2c6e3f65b | ||
|
|
1ff3dcf138 | ||
|
|
9b564431b0 | ||
|
|
65058db89f | ||
|
|
487d2a9221 | ||
|
|
6151debeab | ||
|
|
e05ef230a7 | ||
|
|
685755fe64 | ||
|
|
465ece5d25 | ||
|
|
10d1361a2c | ||
|
|
34617270a5 | ||
|
|
29aa370aa6 | ||
|
|
1ef7fd3659 | ||
|
|
6650072fe6 | ||
|
|
951bd3a517 | ||
|
|
2e3ddd3a61 | ||
|
|
dd628514c6 | ||
|
|
c74686a197 | ||
|
|
c42dbbfec8 | ||
|
|
ea3678f8c7 | ||
|
|
51099bf148 | ||
|
|
924e0261f9 | ||
|
|
dc5eda7ebb | ||
|
|
aec6636205 | ||
|
|
a18c3ff07d | ||
|
|
64928b28bc | ||
|
|
0bbaa51945 | ||
|
|
561803c0a7 | ||
|
|
1a5279bd6e | ||
|
|
534ddf01db | ||
|
|
937d5b5472 | ||
|
|
03a407d4df | ||
|
|
3508e1e6d5 | ||
|
|
145b1f4fb3 | ||
|
|
09bce5e6d6 | ||
|
|
51447961cb | ||
|
|
89e73594eb | ||
|
|
0cddc8a154 | ||
|
|
8152309435 | ||
|
|
a3089a719f | ||
|
|
2cb0b00e3a | ||
|
|
2aa219e150 | ||
|
|
93740c997c | ||
|
|
264ec5bab7 | ||
|
|
e340593ad1 | ||
|
|
05b6afcd17 | ||
|
|
10994d65be | ||
|
|
a18ce275ab | ||
|
|
66c39a953a | ||
|
|
09fb9dcd92 | ||
|
|
eb5c2e9823 | ||
|
|
50d5c4b9ca | ||
|
|
54708c6131 | ||
|
|
0e9ba9a004 | ||
|
|
08f10d3cea | ||
|
|
c2dd7e0f6e | ||
|
|
57fc5d28a9 | ||
|
|
49bdcad4b6 | ||
|
|
ef7d259bb7 | ||
|
|
688fe131c9 | ||
|
|
68b42a00e0 | ||
|
|
7368a790a3 | ||
|
|
527e1b8a16 | ||
|
|
f6d6658de7 | ||
|
|
6a929d6a1a | ||
|
|
ae380cb21e | ||
|
|
cf4c603362 | ||
|
|
d83c9da830 | ||
|
|
9a7c9e8d98 | ||
|
|
ace947e6b3 | ||
|
|
5f9378569b | ||
|
|
a180b95cca | ||
|
|
73bbe25d26 | ||
|
|
fc5e3a0aec | ||
|
|
7625d66259 | ||
|
|
30f2c2bd77 | ||
|
|
e3052c8c57 | ||
|
|
7b6ebc5c98 | ||
|
|
0093dc3d32 | ||
|
|
ccefd3be02 | ||
|
|
d509ef7581 | ||
|
|
5e165819a1 | ||
|
|
d180f01b45 | ||
|
|
91c09dd0a0 | ||
|
|
f6ca92337b | ||
|
|
ade9d662db | ||
|
|
baca196da6 | ||
|
|
10801bfb25 | ||
|
|
a89a62071b | ||
|
|
657051e4b6 | ||
|
|
f6f6d89e06 | ||
|
|
1a6af5d896 | ||
|
|
a4b83895a3 | ||
|
|
4f03548608 | ||
|
|
773e1d4e05 | ||
|
|
d9ef8c1779 | ||
|
|
f9e3a2132f | ||
|
|
9ab318cafc | ||
|
|
1a126b700f | ||
|
|
92dd5e1610 | ||
|
|
11ecbd27be | ||
|
|
585ef998d0 | ||
|
|
c297aff99b | ||
|
|
68142c9835 | ||
|
|
b8f4fa359c | ||
|
|
2b765fdedb | ||
|
|
0accdb9e34 | ||
|
|
5d468f7ca5 | ||
|
|
83768f0531 | ||
|
|
acae0ac2a6 | ||
|
|
0f86651089 | ||
|
|
a77f2fa424 | ||
|
|
6198e43fe6 | ||
|
|
621d4745b4 | ||
|
|
dac75a1dda | ||
|
|
f1c0f3d45f | ||
|
|
08b6a2c36d | ||
|
|
130b67821c | ||
|
|
b902769fa8 | ||
|
|
7503a58d1f | ||
|
|
096ce1a207 | ||
|
|
d1ef039e39 | ||
|
|
040ca5320d | ||
|
|
625c85291d | ||
|
|
b28835a604 | ||
|
|
6340704173 | ||
|
|
522b7c51f7 | ||
|
|
388ff75260 | ||
|
|
37ef0a0cb6 | ||
|
|
d7be2c5b8a | ||
|
|
b50e701cae | ||
|
|
b7a0443091 | ||
|
|
a8f0fd1fff | ||
|
|
a89d50d34e | ||
|
|
6489cfbce6 | ||
|
|
d10f40f109 | ||
|
|
175b56b43c | ||
|
|
9b68a3ef4a | ||
|
|
345d6c5687 | ||
|
|
5b1b5cc8f2 | ||
|
|
1b062ab929 | ||
|
|
a7420ca3d4 | ||
|
|
4dcd1f3e65 | ||
|
|
e46b392a8d | ||
|
|
47ff768b70 | ||
|
|
ba4bd3ed55 | ||
|
|
68428a5d5e | ||
|
|
28694e8afb | ||
|
|
c420a0ae2b | ||
|
|
a85e50f465 | ||
|
|
4962cc3da8 | ||
|
|
bb703281a2 | ||
|
|
52f5564122 | ||
|
|
1dc420ce51 | ||
|
|
20af1ceb7d | ||
|
|
ec982ccacb | ||
|
|
a97e74b2d4 | ||
|
|
fecc5b09f8 | ||
|
|
d1030eb0b2 | ||
|
|
fb749dc526 | ||
|
|
e6df1828d9 | ||
|
|
9ff53fea98 | ||
|
|
4f1888d2ea | ||
|
|
53dcd0dee9 | ||
|
|
e291ada371 | ||
|
|
9c9fed749a | ||
|
|
61556a54e2 | ||
|
|
79e2f8a2e5 | ||
|
|
d1f39e6217 | ||
|
|
4aa488f48b | ||
|
|
2d72b25c43 | ||
|
|
f23b0aacd7 | ||
|
|
98d27c4a6a | ||
|
|
d01ab227b8 | ||
|
|
0cfeee4ded | ||
|
|
c97e43dcd6 | ||
|
|
eda321954d | ||
|
|
64e5392788 | ||
|
|
987f95221c | ||
|
|
6e917d156c | ||
|
|
aeed287122 | ||
|
|
36e697b344 | ||
|
|
c2d0d4d28c | ||
|
|
ea6a3c0963 | ||
|
|
8902a5c5cd | ||
|
|
3021c5cfad | ||
|
|
16db9a7337 | ||
|
|
aec9c3c9a4 | ||
|
|
0daa225e26 | ||
|
|
85be2b85fd | ||
|
|
a7d6146169 | ||
|
|
978ec91107 | ||
|
|
297859c5bc | ||
|
|
bea52aa743 | ||
|
|
54f1be69c7 | ||
|
|
a4cc9ef2cc | ||
|
|
4f47594b6d | ||
|
|
189a7766d4 | ||
|
|
df9174577a | ||
|
|
bd1bb7a71b | ||
|
|
4b6e7e6c37 | ||
|
|
8896642e25 | ||
|
|
0ecb5a3fec | ||
|
|
d289b0b450 | ||
|
|
b8489464b3 | ||
|
|
ebaa3f39e4 | ||
|
|
e19753dcde | ||
|
|
13255a3762 | ||
|
|
15ce2a3d67 | ||
|
|
3bb97b81de | ||
|
|
9247780073 | ||
|
|
37161d3017 | ||
|
|
4532037e4f | ||
|
|
0fe08e1b33 | ||
|
|
236acbd6e8 | ||
|
|
16b0704acc | ||
|
|
61bcd67a5d | ||
|
|
0629c2a086 | ||
|
|
fbdc5a0eb5 | ||
|
|
68a8d81b6a | ||
|
|
a368301dbf | ||
|
|
89989adcad | ||
|
|
95cdb4b2bc | ||
|
|
228c835466 | ||
|
|
d604166194 | ||
|
|
d643a2ff13 | ||
|
|
b581a171f0 | ||
|
|
307336cfc4 | ||
|
|
fc30171725 | ||
|
|
34be7e99f0 | ||
|
|
7679df062c | ||
|
|
71cfd874ae | ||
|
|
08be0c374a | ||
|
|
7decce9718 | ||
|
|
22bab90a90 | ||
|
|
02882fb327 | ||
|
|
c74d597c84 | ||
|
|
653c77e852 | ||
|
|
2b36f4f57f | ||
|
|
82b0ebb787 | ||
|
|
3f35006c26 | ||
|
|
2ffd8637e1 | ||
|
|
44c1572b8f | ||
|
|
d0d749074e | ||
|
|
dbc44c08df | ||
|
|
46fbd7f1e1 | ||
|
|
5048c6c22a | ||
|
|
709d82e764 | ||
|
|
9d64b35ed8 | ||
|
|
0f00862e5e | ||
|
|
532e79c7d0 | ||
|
|
4ebad10557 | ||
|
|
0b04a7f17f | ||
|
|
77f96b386e | ||
|
|
ea86ddc693 | ||
|
|
e3e43d0ba0 | ||
|
|
b10929fe23 | ||
|
|
f512cb8e35 | ||
|
|
4f240f538d | ||
|
|
db6db6a4e9 | ||
|
|
f2add8de94 | ||
|
|
88c6621cfe | ||
|
|
c152b6f0ad | ||
|
|
53c0188248 | ||
|
|
725addafda | ||
|
|
19628c4732 | ||
|
|
04eaf7f175 | ||
|
|
f5850d0c08 | ||
|
|
855eb8355a | ||
|
|
fdbb7fd30f | ||
|
|
30d0ac0784 | ||
|
|
3d5c75420a | ||
|
|
1d5967d143 | ||
|
|
110a41d18d | ||
|
|
a3d8b9935a | ||
|
|
08357e3cb0 | ||
|
|
162a445a50 | ||
|
|
c7257bcf46 | ||
|
|
dfca8c09e0 | ||
|
|
7690f73e81 | ||
|
|
86fbb5952e | ||
|
|
78c92642e4 | ||
|
|
b3f6129718 | ||
|
|
2a52603b7e | ||
|
|
e6f9f258ec | ||
|
|
22e7b4c911 | ||
|
|
af5f7a7779 | ||
|
|
693d692a47 | ||
|
|
81f0189d23 | ||
|
|
e7f7e96d58 | ||
|
|
1dab2ac7d3 | ||
|
|
ec54074392 | ||
|
|
23b4c9c667 | ||
|
|
472ed721a3 | ||
|
|
ed3f2646f0 | ||
|
|
0499d2b5c4 | ||
|
|
81ba629b56 | ||
|
|
0bb746ba39 | ||
|
|
16a0f40ac2 | ||
|
|
f84103918a | ||
|
|
b5ca9bbab2 | ||
|
|
ff38d2bba6 | ||
|
|
8f2a8a0051 | ||
|
|
97147b594b | ||
|
|
9ff6d6e7b5 | ||
|
|
a0ec5b18e7 | ||
|
|
f2acdd27fd | ||
|
|
4ade446b55 | ||
|
|
ec6569fbea | ||
|
|
1ad6742dbc | ||
|
|
63ea3e8d27 | ||
|
|
9ace7db216 | ||
|
|
841513501a | ||
|
|
5c94147603 | ||
|
|
02f6d4cb66 | ||
|
|
ec5fad433c | ||
|
|
b4fa97fd54 | ||
|
|
d30b441ede | ||
|
|
dda29f7e2f | ||
|
|
952e281993 | ||
|
|
1fa026b9c7 | ||
|
|
e8c91e6e12 | ||
|
|
41425f7f74 | ||
|
|
2cf01c23a2 | ||
|
|
412f85b665 | ||
|
|
ebc90f6ab8 | ||
|
|
84d80e93bc | ||
|
|
b7e92dbced | ||
|
|
44dcb0d0a9 | ||
|
|
b3e3e080a9 | ||
|
|
1167cdcaec | ||
|
|
4eda39a31d | ||
|
|
759b75ca48 | ||
|
|
55e862a4a4 | ||
|
|
227547f826 | ||
|
|
7c41dd5e31 | ||
|
|
a964646803 | ||
|
|
9dac02ba5d | ||
|
|
43877d2647 | ||
|
|
cc6159b39b | ||
|
|
43ff787b04 | ||
|
|
3633598462 | ||
|
|
94922f2df6 | ||
|
|
75fe022f96 | ||
|
|
bc839569fb | ||
|
|
f62a4a0c0c | ||
|
|
8dacd51abb | ||
|
|
06302871bc | ||
|
|
c1ec2afeca | ||
|
|
e2f1338f94 | ||
|
|
5f9b57d300 | ||
|
|
ddf77f10e9 | ||
|
|
127532c226 | ||
|
|
572adbaad2 | ||
|
|
f4c7822bc9 | ||
|
|
89561b8d45 | ||
|
|
b0775f7a58 | ||
|
|
2dc50e6633 | ||
|
|
ad2a3d603e | ||
|
|
545f23551f | ||
|
|
b15c1ffedc | ||
|
|
56d70e4ea7 | ||
|
|
0b934232fd | ||
|
|
a4a53e1355 | ||
|
|
c445e70cff | ||
|
|
e8eec2cb41 | ||
|
|
dd6fa4af00 | ||
|
|
afdb9a63ff | ||
|
|
10ba2cd312 | ||
|
|
4c1fa9c242 | ||
|
|
3c6b707353 | ||
|
|
96efc8c7f0 | ||
|
|
0cd6afde6f | ||
|
|
82b11da4ca | ||
|
|
4ec39ab707 | ||
|
|
d2a60f3ca4 | ||
|
|
3bc6628227 | ||
|
|
27579e0701 | ||
|
|
f91bcfeb4b | ||
|
|
e19809d5b5 | ||
|
|
dd068467de | ||
|
|
3099c799b2 | ||
|
|
cd3ef8fa5a | ||
|
|
72ce37704b | ||
|
|
4420d073bb | ||
|
|
9cc9f519fc | ||
|
|
0483d841e3 | ||
|
|
5546120312 | ||
|
|
ad613e2437 | ||
|
|
c544759d36 | ||
|
|
20503d3c58 | ||
|
|
3fb17c5de8 | ||
|
|
a5e4bf16d3 | ||
|
|
68d9aad3a2 | ||
|
|
0aba1b4ad3 | ||
|
|
e12c7c8d27 | ||
|
|
83a040722e | ||
|
|
b7b504d43a | ||
|
|
b18804f57f | ||
|
|
550a5fb4c0 | ||
|
|
40f0238bb7 | ||
|
|
089823785e | ||
|
|
ecf7dded07 | ||
|
|
909aba27d1 | ||
|
|
cd4f29135b | ||
|
|
68c5c366f4 | ||
|
|
29a5311ae0 | ||
|
|
62d774a548 | ||
|
|
c9baca7910 | ||
|
|
86366ae157 | ||
|
|
c4094c68ee | ||
|
|
ec67a1b2c1 | ||
|
|
7ba9a5972d | ||
|
|
b32071ad04 | ||
|
|
b38c4e1a28 | ||
|
|
a13b2b4018 | ||
|
|
65a2f789dc | ||
|
|
8bd12ed040 | ||
|
|
6914662dd8 | ||
|
|
bcb11d9b7e | ||
|
|
920cab6f12 | ||
|
|
9756adb933 | ||
|
|
2671af13cd | ||
|
|
b7b94e38ac | ||
|
|
37792e9b38 | ||
|
|
f90bf756fb | ||
|
|
0b363a5c98 | ||
|
|
ebb1a8af1b | ||
|
|
f4ba7fcaf4 | ||
|
|
13964ac726 | ||
|
|
c8c1140f15 | ||
|
|
9a473640fb | ||
|
|
405173a0b4 | ||
|
|
8e43b86f06 | ||
|
|
eea9aaf940 | ||
|
|
67d3e8d049 | ||
|
|
f99ca918db | ||
|
|
79a0a66f1f | ||
|
|
08681f4a8b | ||
|
|
a58ef94a9c | ||
|
|
2b9ebd6662 | ||
|
|
6fdd2f40ed | ||
|
|
238990a285 | ||
|
|
1a77490969 | ||
|
|
5431d05168 | ||
|
|
eb97cdc33a | ||
|
|
b54d6589c3 | ||
|
|
463768fcf7 | ||
|
|
a43545c6ea | ||
|
|
9f067d7f56 | ||
|
|
8907e2d850 | ||
|
|
c84466b131 | ||
|
|
f62b956e74 | ||
|
|
9841063df9 | ||
|
|
803c8177d3 | ||
|
|
7ff525468f | ||
|
|
7c1c36f043 | ||
|
|
565e3f75c7 | ||
|
|
842f030355 | ||
|
|
f4ad42bb84 | ||
|
|
fb08b53f0b | ||
|
|
598becf619 | ||
|
|
9a27b38976 | ||
|
|
7917aa2a7c | ||
|
|
d0c97a589b | ||
|
|
8ec1ea7b7a | ||
|
|
71cb6d2bce | ||
|
|
ee258f1425 | ||
|
|
50278674f0 | ||
|
|
be5085f205 | ||
|
|
ca9476f72a | ||
|
|
d55c64c838 | ||
|
|
563de2cc90 | ||
|
|
6afe3ccc3b | ||
|
|
14ad5955b5 | ||
|
|
a6f2110141 | ||
|
|
393d8b9ded | ||
|
|
137dc1eac0 | ||
|
|
5fee82ce39 | ||
|
|
552710ac2a | ||
|
|
0a9a11636a | ||
|
|
e550631275 | ||
|
|
87d2f7f27a | ||
|
|
a40cd2b46f | ||
|
|
4b581f3720 | ||
|
|
12956679e7 | ||
|
|
7903fcb48c | ||
|
|
9672c6b885 | ||
|
|
55369d30a6 | ||
|
|
a207199879 | ||
|
|
a894b7cc9b | ||
|
|
46b3a9158c | ||
|
|
26421684dc | ||
|
|
fd536d373e | ||
|
|
4f59a821d3 | ||
|
|
b29e21efa8 | ||
|
|
a63dc75b43 | ||
|
|
9f6f721a13 | ||
|
|
fdb9d93b12 | ||
|
|
475e6e28eb | ||
|
|
4c1f70af4b | ||
|
|
454c90820d | ||
|
|
0a4e61c1dd | ||
|
|
31d9ba7e02 | ||
|
|
6a81b0f807 | ||
|
|
3322630732 | ||
|
|
d8885984ab | ||
|
|
4a18c45e4f | ||
|
|
5b7cac1002 | ||
|
|
5aef9266cb | ||
|
|
80b40c02b4 | ||
|
|
56d6079c4a | ||
|
|
3975792bf1 | ||
|
|
697e694de6 | ||
|
|
828d8eaadb | ||
|
|
30ee00ff50 | ||
|
|
7e130c2618 | ||
|
|
b859dd660c | ||
|
|
8868783476 | ||
|
|
dc0dd6588c | ||
|
|
f7e7e885a9 | ||
|
|
d813be1f48 | ||
|
|
0d03309c2f | ||
|
|
1dab353fdb | ||
|
|
8eb4efaddb | ||
|
|
12c900ea7d | ||
|
|
c31db83b26 | ||
|
|
98ce8f4c2f | ||
|
|
1f25b4a8a9 | ||
|
|
1756bbff84 | ||
|
|
21b2ffa42e | ||
|
|
b6efdac1db | ||
|
|
fc9d321ebe | ||
|
|
68a290c347 | ||
|
|
9133de50e9 | ||
|
|
73d04b976e | ||
|
|
a35d271669 | ||
|
|
72a7f932c6 | ||
|
|
2e74df2583 | ||
|
|
4fffb3c816 | ||
|
|
840b3a34cb | ||
|
|
dc267663a7 | ||
|
|
8d6443b25d | ||
|
|
e3c7fc8077 | ||
|
|
bbf2a15f27 | ||
|
|
5a326b82bd | ||
|
|
f1b0dd7836 | ||
|
|
c205777542 | ||
|
|
329a1e6f16 | ||
|
|
9c39121e99 | ||
|
|
e5e57e684e | ||
|
|
659a60aeec | ||
|
|
b0268adad9 | ||
|
|
71e4bbfc99 | ||
|
|
c6bd004d63 | ||
|
|
8b6986ba18 | ||
|
|
b23718f3ad | ||
|
|
8113548920 | ||
|
|
b401dbbf65 | ||
|
|
75dd0a770f | ||
|
|
6d8292cdd8 | ||
|
|
0575eb671a | ||
|
|
d06eea53ef | ||
|
|
9e96a93172 | ||
|
|
441f8f3ce8 | ||
|
|
1d4dec5510 | ||
|
|
167758003c | ||
|
|
0a3ac1f5c3 | ||
|
|
9173140ddf | ||
|
|
7aeb113c62 | ||
|
|
22cd408efb | ||
|
|
4fbd21da57 | ||
|
|
68f66ca101 | ||
|
|
ed95509a4f | ||
|
|
4162975f9f | ||
|
|
ac0cdcf70b | ||
|
|
63134fafec | ||
|
|
5494e88e08 | ||
|
|
86276ad17b | ||
|
|
c883ec40d7 | ||
|
|
d19bc328f3 | ||
|
|
f286f904dc | ||
|
|
150029a5e2 | ||
|
|
709a3fb06f | ||
|
|
d3c9d0b331 | ||
|
|
cc2d59468d | ||
|
|
91391fba5d | ||
|
|
4369402855 | ||
|
|
0bc556618f | ||
|
|
cb11580981 | ||
|
|
0366e8758c | ||
|
|
8d230dd798 | ||
|
|
411b342a27 | ||
|
|
b9b7032386 | ||
|
|
c38ef9023b | ||
|
|
9cecd525e2 | ||
|
|
4f5995abc0 | ||
|
|
2945b230e4 | ||
|
|
261cc448f7 | ||
|
|
616b0b6baa | ||
|
|
d99968ee6d | ||
|
|
4a65ff6ae2 | ||
|
|
94f91ae687 | ||
|
|
28e4bcf67f | ||
|
|
884461f1a6 | ||
|
|
26c669e42d | ||
|
|
f60dde4138 | ||
|
|
9c545059ae | ||
|
|
05dea7b22a | ||
|
|
5b3f915d90 | ||
|
|
d987d61ea9 | ||
|
|
dedb56d295 | ||
|
|
8697972d5d | ||
|
|
f90a2ae195 | ||
|
|
c34aadfbf7 | ||
|
|
084de9d8e0 | ||
|
|
69b780ee32 | ||
|
|
c7b904501c | ||
|
|
206be3c161 | ||
|
|
48e8022095 | ||
|
|
4715a1a5e0 | ||
|
|
ed2ba6bc3a | ||
|
|
ca1d62bec0 | ||
|
|
6a4aad1aa8 | ||
|
|
3f0462b68b | ||
|
|
f3a622d1a7 | ||
|
|
3cd5b9ca2e | ||
|
|
e9782c3219 | ||
|
|
8d348954a7 | ||
|
|
7d19d784df | ||
|
|
733b4e0a34 | ||
|
|
08d29a8342 | ||
|
|
675e2d25d6 | ||
|
|
ba20af48d3 | ||
|
|
707e053949 | ||
|
|
c47e67e52c | ||
|
|
3a95bfb699 | ||
|
|
6567bb4c12 | ||
|
|
39ba697e19 | ||
|
|
c9818ea2c4 | ||
|
|
afe5cb588d | ||
|
|
e9e999542d | ||
|
|
d9db90752e | ||
|
|
f7c3f52817 | ||
|
|
681e3785ef | ||
|
|
5f593994c7 | ||
|
|
ec73aeba16 | ||
|
|
7573e560b6 | ||
|
|
c8bc155cfe | ||
|
|
1eae73105a | ||
|
|
360dc140ea | ||
|
|
f85348ba94 | ||
|
|
2f15ad4be0 | ||
|
|
f254bb39a5 | ||
|
|
c58465d630 | ||
|
|
03a1386902 | ||
|
|
70b56eb527 | ||
|
|
4e05062def | ||
|
|
266333468b | ||
|
|
e32b3aac22 | ||
|
|
676402d918 | ||
|
|
edb4d066a9 | ||
|
|
03f4518da9 | ||
|
|
8259e82787 | ||
|
|
838d3ddc17 | ||
|
|
66686de4e4 | ||
|
|
ce8dca7afe | ||
|
|
9f5ef4c1cb | ||
|
|
f0a87da375 | ||
|
|
263e30d25d | ||
|
|
15ffc30d88 | ||
|
|
8a5c4979ad | ||
|
|
e36fbd6af5 | ||
|
|
3216806fae | ||
|
|
f8526f027c | ||
|
|
ed3066aae7 | ||
|
|
98a7e72f0a | ||
|
|
a2259865b3 | ||
|
|
63f3283591 | ||
|
|
d670ea4f59 | ||
|
|
d0d10bc6e7 | ||
|
|
4fea06c9fa | ||
|
|
09fed60dec | ||
|
|
75b9c39b0e | ||
|
|
5957a1068f | ||
|
|
df5229c7c8 | ||
|
|
ed817c81de | ||
|
|
dd72f7638d | ||
|
|
a77e4aa6fa | ||
|
|
792f3775ce | ||
|
|
4c7700ec3b | ||
|
|
eee296c4c2 | ||
|
|
499f745732 | ||
|
|
446388e0ba | ||
|
|
e1628bcdd8 | ||
|
|
6d5874fc45 | ||
|
|
3d563dea87 | ||
|
|
09304c33c1 | ||
|
|
521d8c4b1f | ||
|
|
b7d573a4b8 | ||
|
|
4a62385dcc | ||
|
|
98e15f658e | ||
|
|
0a3fa35c5d | ||
|
|
02e095bec2 | ||
|
|
696d9c6bd3 | ||
|
|
50dee5d464 | ||
|
|
87a8dda955 | ||
|
|
d7c73f590c | ||
|
|
d1b197e339 | ||
|
|
8f5ee989ba | ||
|
|
ce9c227425 | ||
|
|
e32c2b84ee | ||
|
|
ce9f77afed | ||
|
|
2bc38b2063 | ||
|
|
b4f4c28871 | ||
|
|
b14ef537e1 | ||
|
|
84ac386481 | ||
|
|
f0365d32aa | ||
|
|
795764f22f | ||
|
|
986f61ac92 | ||
|
|
09576f2f4f | ||
|
|
bba474dc6b | ||
|
|
fde971fe81 | ||
|
|
882e1db1d6 | ||
|
|
6b0333e919 | ||
|
|
914808b867 | ||
|
|
8d38cf4d1f | ||
|
|
2d1d512d0f | ||
|
|
37bc099d39 | ||
|
|
9e3c931b34 | ||
|
|
f8fb0e67b4 | ||
|
|
86ef6e6987 | ||
|
|
e8fd373e6c | ||
|
|
7efa546665 | ||
|
|
4d2a0697ed | ||
|
|
c6023782a4 | ||
|
|
30283282d2 | ||
|
|
7588fc0989 | ||
|
|
d698c1093a | ||
|
|
7b327d47c0 | ||
|
|
e629985cf4 | ||
|
|
aa875f1147 | ||
|
|
fe843bc466 | ||
|
|
5b355c6ca7 | ||
|
|
a3f7ff90e3 | ||
|
|
1f3f8a5073 | ||
|
|
9c88971bc1 | ||
|
|
aad309ee4f | ||
|
|
e80ca4ddbc | ||
|
|
28ccad28c2 | ||
|
|
ba9e7fbf64 | ||
|
|
28c85cf8e7 | ||
|
|
526b5a8d25 | ||
|
|
6b15cf3f72 | ||
|
|
fbd8ab47ea | ||
|
|
893917a25d | ||
|
|
af5ff2bb93 | ||
|
|
8995d3434f | ||
|
|
5f9b0675e2 | ||
|
|
46ac97a3ff | ||
|
|
db3264ab8c | ||
|
|
a0923622ae | ||
|
|
aa9975ad0d | ||
|
|
6787c81abe | ||
|
|
72205176e1 | ||
|
|
480742cc15 | ||
|
|
48bdfa2377 | ||
|
|
2ff6f4d3cf | ||
|
|
ae32938531 | ||
|
|
d3da603292 | ||
|
|
912bcf9487 | ||
|
|
413f071861 | ||
|
|
668c43abf3 | ||
|
|
43e9553ebc | ||
|
|
e8b54a5087 | ||
|
|
39852662a6 | ||
|
|
6a66ba8a21 | ||
|
|
36a7a84080 | ||
|
|
7e0b334b38 | ||
|
|
5d8d217a13 | ||
|
|
f2aa5c0235 | ||
|
|
323febe8c7 | ||
|
|
32d8f349c9 | ||
|
|
3910495cce | ||
|
|
fe69afdefb | ||
|
|
9082862b9d | ||
|
|
cb4a2cf029 | ||
|
|
26b5180bf7 | ||
|
|
11bfb1e5fd | ||
|
|
e431df06ab | ||
|
|
cbf0ceacd5 | ||
|
|
8c634d8323 | ||
|
|
4ae108009c | ||
|
|
a5c1c30368 | ||
|
|
8cd3086be0 | ||
|
|
dd37ae26a5 | ||
|
|
fdaebc7365 | ||
|
|
a8b62261f6 | ||
|
|
47eb913c22 | ||
|
|
7a46293f7a | ||
|
|
6b26d2b62d | ||
|
|
cac3b3ea35 | ||
|
|
dff4d03bd4 | ||
|
|
28d83d42e2 | ||
|
|
38f1b4d205 | ||
|
|
931d19eece | ||
|
|
88bbe55b85 | ||
|
|
dd17124ec6 | ||
|
|
674b50889e | ||
|
|
263c38caec | ||
|
|
3881f22192 | ||
|
|
664446631f | ||
|
|
c5f1cca3a0 | ||
|
|
a7407097e1 | ||
|
|
14bb60c61f | ||
|
|
749c0e51e6 | ||
|
|
0f120c41f1 | ||
|
|
65a7d56957 | ||
|
|
8ab8a6eefb | ||
|
|
52351d7dc8 | ||
|
|
224e0c298a | ||
|
|
a5a0e564dd | ||
|
|
b8c94fc7cf | ||
|
|
62dd3a5380 | ||
|
|
e3ddb677e1 | ||
|
|
32d7bd5ab1 | ||
|
|
861df49670 | ||
|
|
716f727753 | ||
|
|
7e381f8e5d | ||
|
|
183063a244 | ||
|
|
ef871775b7 | ||
|
|
45e386b26d | ||
|
|
e1db5db8ac | ||
|
|
c3a289cebc | ||
|
|
20e51f0b4d | ||
|
|
464dc93751 | ||
|
|
7445a3be59 | ||
|
|
2bbc25c1eb | ||
|
|
fd56fe6eb2 | ||
|
|
92dfa8becc | ||
|
|
ae8f9561ad | ||
|
|
3052ba433a | ||
|
|
508012342d | ||
|
|
9fa207e613 | ||
|
|
2d7b9817cb | ||
|
|
e8d808d708 | ||
|
|
d71595fc75 | ||
|
|
399d6592b8 | ||
|
|
628a6ffa07 | ||
|
|
3e1a94cbcd | ||
|
|
a6c2d4b0e2 | ||
|
|
19277aec87 | ||
|
|
14c2755436 | ||
|
|
50a9145386 | ||
|
|
9046509b95 | ||
|
|
61eaa44cf8 | ||
|
|
2d1e9abb60 | ||
|
|
d064260bf1 | ||
|
|
a19ad3fd1d | ||
|
|
3bc59a0327 | ||
|
|
5a883889a2 | ||
|
|
fbaa7a4d67 | ||
|
|
6baa6d488b | ||
|
|
e26f9b8095 | ||
|
|
f92fae7625 | ||
|
|
3d0e269241 | ||
|
|
c17c47f18a | ||
|
|
98b8bfb3fa | ||
|
|
83b1a98db1 | ||
|
|
f49f55f4a5 | ||
|
|
e58d19b420 | ||
|
|
58f753136a | ||
|
|
22d827adf1 | ||
|
|
d3de50e0f9 | ||
|
|
432037d20d | ||
|
|
86ef0a2609 | ||
|
|
bae50da799 | ||
|
|
2c45f27356 | ||
|
|
759f4f2c62 | ||
|
|
28355335f8 | ||
|
|
9f6832d636 | ||
|
|
5309afc347 | ||
|
|
693627a858 | ||
|
|
48eaa0e5bf | ||
|
|
9ad7ac632a | ||
|
|
41e3ecad46 | ||
|
|
d6f8d63742 | ||
|
|
28b0929554 | ||
|
|
55787ff7b9 | ||
|
|
b00919c692 | ||
|
|
d43392628b | ||
|
|
aa831fee5b | ||
|
|
5c568d6999 | ||
|
|
d84665cb64 | ||
|
|
4bb5d27c59 | ||
|
|
11a9f1d1f0 | ||
|
|
123e8f21b5 | ||
|
|
6d6b2efdb5 | ||
|
|
94b925f5ef | ||
|
|
c86755f1ab | ||
|
|
b8418ced44 | ||
|
|
fac0beaa0a | ||
|
|
c1f5229906 | ||
|
|
d5865989cf | ||
|
|
90e587a974 | ||
|
|
4a6b31fbe2 | ||
|
|
a63766a005 | ||
|
|
47359c1a3b | ||
|
|
abd0dad2bf | ||
|
|
54a52f7048 | ||
|
|
f213215c81 | ||
|
|
96cde120b4 | ||
|
|
0096ef4ddb | ||
|
|
ce6c7d4b59 | ||
|
|
b51ed9bbb7 | ||
|
|
5c6af92a0d | ||
|
|
d8eb08e214 | ||
|
|
c6f5c7f1a3 | ||
|
|
a6b6e31cda | ||
|
|
28145a9deb | ||
|
|
fa991c8501 | ||
|
|
5f345d2089 | ||
|
|
520c454f22 | ||
|
|
cc81668c8f | ||
|
|
6426e044d6 | ||
|
|
7b92371a03 | ||
|
|
6105d4a4e6 | ||
|
|
84649e9d20 | ||
|
|
1f7df33e28 | ||
|
|
3c394f08b0 | ||
|
|
64f07d9bf3 | ||
|
|
849a6c12be | ||
|
|
9144ce746e | ||
|
|
64821ad4f5 | ||
|
|
2655e726c9 | ||
|
|
de2970d7ef | ||
|
|
012dd6986b | ||
|
|
2ef9904d00 | ||
|
|
2c83224f07 | ||
|
|
0c63090a23 | ||
|
|
0159277dbf | ||
|
|
6ca5f3d8f6 | ||
|
|
875625b147 | ||
|
|
da0bd5a9dc | ||
|
|
ce02ad641b | ||
|
|
78915896d5 | ||
|
|
4a5d2e16d0 | ||
|
|
3e3161c747 | ||
|
|
694af4aeb1 | ||
|
|
7f59d7ea48 | ||
|
|
52b945164c | ||
|
|
584fb2904b | ||
|
|
71013b372d | ||
|
|
338b3ba590 | ||
|
|
6b798b01a8 | ||
|
|
7df20e5049 | ||
|
|
7128d79935 | ||
|
|
e27dfbb0bb | ||
|
|
a51f109930 | ||
|
|
3e101521dd | ||
|
|
726c7a4d32 | ||
|
|
767f05cfa7 | ||
|
|
47b49f1be9 | ||
|
|
a4964b9073 | ||
|
|
40e2ec3ae9 | ||
|
|
120cde169b | ||
|
|
1c35f46b45 | ||
|
|
eea713eed2 | ||
|
|
8c88757451 | ||
|
|
dd171ca44a | ||
|
|
cfd086a140 | ||
|
|
72fe7396d6 | ||
|
|
03140865f0 | ||
|
|
a4fc802d1b | ||
|
|
b6d760b903 | ||
|
|
e4b24d20ac | ||
|
|
91607bb2a1 | ||
|
|
fce8223663 | ||
|
|
d1067c60bf | ||
|
|
441c26dd32 | ||
|
|
00781dd4e1 | ||
|
|
c7b8debb6e | ||
|
|
d51c383866 | ||
|
|
f7d4698ef0 | ||
|
|
3fdbbafcb5 | ||
|
|
c82cd90ed6 | ||
|
|
b6fc8398cf | ||
|
|
e90f3b84c1 | ||
|
|
9134b6ea98 | ||
|
|
4a380ad7fc | ||
|
|
c05eb0b1b2 | ||
|
|
66feebfc0e | ||
|
|
04a609b51f | ||
|
|
258cf20c92 | ||
|
|
41c8d88217 | ||
|
|
37f39c0870 | ||
|
|
6ba4f8b54c | ||
|
|
c1f8ffa386 | ||
|
|
97893d293b | ||
|
|
78d1cfb464 | ||
|
|
b5fdfe27d5 | ||
|
|
cd9fb3b635 | ||
|
|
9e9f839d96 | ||
|
|
60814ecfe1 | ||
|
|
0170c20e9a | ||
|
|
01cc2e13d8 | ||
|
|
f823f170e6 | ||
|
|
be186bd39b | ||
|
|
ea25492c28 | ||
|
|
79a2bed640 | ||
|
|
cd8fc35968 | ||
|
|
d2cde379ad | ||
|
|
506c41cb15 | ||
|
|
72f54ca6c1 | ||
|
|
f8d22c486e | ||
|
|
c1151b0d45 | ||
|
|
0e65fdd6f7 | ||
|
|
3164b5ab13 | ||
|
|
e6cda79ee8 | ||
|
|
45e21d5000 | ||
|
|
7e212c4d40 | ||
|
|
775aae7082 | ||
|
|
a01da2fd92 | ||
|
|
cd2fe698bb | ||
|
|
4f209e8992 | ||
|
|
eb207322d3 | ||
|
|
06a2e5fc82 | ||
|
|
a8ae23d0a2 | ||
|
|
2befb5e784 | ||
|
|
6d5e7826ae | ||
|
|
c99d4948b7 | ||
|
|
ee6f78805f | ||
|
|
9a419bd63f | ||
|
|
4a139934f6 | ||
|
|
2823306810 | ||
|
|
4f3b3a273f | ||
|
|
6ae3911972 | ||
|
|
136aebc009 | ||
|
|
6541492a55 | ||
|
|
59e9750602 | ||
|
|
8ea800205c | ||
|
|
59bb9268a1 | ||
|
|
a582e7c2fb | ||
|
|
6e2669ed1d | ||
|
|
7b8a82ce90 | ||
|
|
a8bad622ff | ||
|
|
9c4f7aa688 | ||
|
|
e75b56073b | ||
|
|
a95ccc7e4c | ||
|
|
8101aceab5 | ||
|
|
1c9b19833c | ||
|
|
488745f378 | ||
|
|
b140e2553b | ||
|
|
ae29929714 | ||
|
|
9f80909f6a | ||
|
|
8201458332 | ||
|
|
7a88d80a10 | ||
|
|
3ced411769 | ||
|
|
f7c346de09 | ||
|
|
731ed6952f | ||
|
|
454ad6f8bd | ||
|
|
8a3b6bf0e6 | ||
|
|
fb6e0658cf | ||
|
|
f9b8d7a9d8 | ||
|
|
f763e1edd7 | ||
|
|
dbc3ad1304 | ||
|
|
4249e13eb4 | ||
|
|
ca7ebd9333 | ||
|
|
3f1e6c128f | ||
|
|
1f635b90e7 | ||
|
|
db3043553c | ||
|
|
f87890cb4b | ||
|
|
5911594906 | ||
|
|
9a1f769828 | ||
|
|
b91c0a0616 | ||
|
|
4a9f607d31 | ||
|
|
a00169451f | ||
|
|
ecba959dd9 | ||
|
|
a8202d4b37 | ||
|
|
657334fb67 | ||
|
|
78712245f7 | ||
|
|
70702e41e9 | ||
|
|
0ca3141088 | ||
|
|
ac0970abba | ||
|
|
9eeebb147f | ||
|
|
dcf8457f4d | ||
|
|
534a5ad688 | ||
|
|
529cbc0379 | ||
|
|
b6aff65997 | ||
|
|
b615cce92d | ||
|
|
aea631d9d2 | ||
|
|
bf942a4cb3 | ||
|
|
ceafe389af | ||
|
|
f62457a24e | ||
|
|
bab4f691c5 | ||
|
|
c7becddb78 | ||
|
|
cc3660e259 | ||
|
|
6e93ff8bca | ||
|
|
212d0f24d8 | ||
|
|
114003406d | ||
|
|
4c99c0127b | ||
|
|
c809b33161 | ||
|
|
3f1c7da15e | ||
|
|
a46695581e | ||
|
|
7902d10a3a | ||
|
|
8aff2bd74c | ||
|
|
352dd907ac | ||
|
|
43f195160e | ||
|
|
872bfe4757 | ||
|
|
70bd493a25 | ||
|
|
bd065838fa | ||
|
|
5f6e3da766 | ||
|
|
ee56b9cd4e | ||
|
|
1a27172f20 | ||
|
|
4ef1159666 | ||
|
|
c924e7c537 | ||
|
|
814bd7cb0d | ||
|
|
5f2d8c0155 | ||
|
|
372f691fd6 | ||
|
|
5f05a452fc | ||
|
|
afe3283c53 | ||
|
|
641a2895a6 | ||
|
|
c73c33f94c | ||
|
|
6c7da215e7 | ||
|
|
754a4a7c8b | ||
|
|
0427e8bbb4 | ||
|
|
c47f6ed30a | ||
|
|
3bdc317fc8 | ||
|
|
20cce349e4 | ||
|
|
5261162fdf | ||
|
|
acf117584b | ||
|
|
7b4bbed553 | ||
|
|
270ce87582 | ||
|
|
2fc0225bc9 | ||
|
|
3536cd336d | ||
|
|
86dd290c1d | ||
|
|
95949b6519 | ||
|
|
6499a7298d | ||
|
|
042e09d29f | ||
|
|
36309e6dbc | ||
|
|
e1ac201de1 | ||
|
|
f0c4e44d2f | ||
|
|
1dc3036822 | ||
|
|
a6b399286e | ||
|
|
856811bd2e | ||
|
|
53273a15bf | ||
|
|
3eeb090578 | ||
|
|
a9726fde19 | ||
|
|
f81d4033fa | ||
|
|
5e864ea3b5 | ||
|
|
8148bfeacf | ||
|
|
1e30718df6 | ||
|
|
72e1eb88d9 | ||
|
|
8ee5ede834 | ||
|
|
bd8b1a2501 | ||
|
|
a098167bdb | ||
|
|
7790208126 | ||
|
|
e52304edb4 | ||
|
|
afdd02a80d | ||
|
|
c73fdd4022 | ||
|
|
4356eefbb1 | ||
|
|
6104680caa | ||
|
|
4373fdf48c | ||
|
|
f9cdfd3e5b | ||
|
|
b6c2fc5a69 | ||
|
|
cc6610edc2 | ||
|
|
7b8ddfdd96 | ||
|
|
443a5ca0c2 | ||
|
|
3794b5cb58 | ||
|
|
6cb5377d73 | ||
|
|
a3a92ff1df | ||
|
|
2068efdb38 | ||
|
|
258ca1b434 | ||
|
|
103fa959cb | ||
|
|
b6d48b7a14 | ||
|
|
412e4e6cf9 | ||
|
|
e6f81173a3 | ||
|
|
d50281453d | ||
|
|
4bb488258d | ||
|
|
f6da19ba83 | ||
|
|
88bb7b780d | ||
|
|
3805e5d37e | ||
|
|
a2d6daaef4 | ||
|
|
48e9006cd1 | ||
|
|
a25a4b5d11 | ||
|
|
309bec474f | ||
|
|
d36440a06d | ||
|
|
2a2f772412 | ||
|
|
6a524bff9d | ||
|
|
5def1169db | ||
|
|
a07395fb56 | ||
|
|
e25a375f43 | ||
|
|
96801e3478 | ||
|
|
90100aa169 | ||
|
|
415f375ce6 | ||
|
|
94e9844179 | ||
|
|
50d238b60f | ||
|
|
5c3b41bd93 | ||
|
|
3e0971c159 | ||
|
|
57740fbd18 | ||
|
|
16bc8f7040 | ||
|
|
c145f24621 | ||
|
|
cd3a4573f2 | ||
|
|
8eab77f3c6 | ||
|
|
4af339424a | ||
|
|
4dd69a8b1a | ||
|
|
baa1160594 | ||
|
|
f438ff4bab | ||
|
|
a25b2af66c | ||
|
|
31b67ab92e | ||
|
|
5303b36913 | ||
|
|
a5c56c547d | ||
|
|
ccf9a9976c | ||
|
|
ab1efd923b | ||
|
|
d6780f9e49 | ||
|
|
8534e3b2f7 | ||
|
|
8e6cf669ad | ||
|
|
577380e98e | ||
|
|
235b5b0c15 | ||
|
|
aefed1d1b9 | ||
|
|
d39649f30d | ||
|
|
1546b7e5a9 | ||
|
|
ff74778dea | ||
|
|
29b21b828b | ||
|
|
a6a0495392 | ||
|
|
3281043e27 | ||
|
|
f7299403f7 | ||
|
|
a0d251a336 | ||
|
|
8ca45d3d03 | ||
|
|
d988542afc | ||
|
|
4285d81ca9 | ||
|
|
4286b2917e | ||
|
|
d09b5cb80e | ||
|
|
90fd18bf42 | ||
|
|
cae6c8e5f5 | ||
|
|
473340c53b | ||
|
|
ae302ee600 | ||
|
|
27934ac4ca | ||
|
|
283ef9adb7 | ||
|
|
377fe5ecde | ||
|
|
c848d3ee22 | ||
|
|
eb6be88fac | ||
|
|
fceb728501 | ||
|
|
a0df46258d | ||
|
|
57d1db58db | ||
|
|
d61b687853 | ||
|
|
42ab7a5d72 | ||
|
|
18cb11dcbf | ||
|
|
e9d5407792 | ||
|
|
a09b2c0074 | ||
|
|
a460ac021f | ||
|
|
bdee66fe29 | ||
|
|
84a251c8c7 | ||
|
|
cea763bb11 | ||
|
|
accbda9d2f | ||
|
|
7d64e141e4 | ||
|
|
6a9b4db448 | ||
|
|
e6a95ecd08 | ||
|
|
10cb7585a7 | ||
|
|
a577c7215f | ||
|
|
e5244cf3c0 | ||
|
|
c4d0aec536 | ||
|
|
796647158e | ||
|
|
266e9d0619 | ||
|
|
9ec54ef89b | ||
|
|
256cb90f3c | ||
|
|
8f3a3b293d | ||
|
|
975a7359a2 | ||
|
|
ac7361a55e | ||
|
|
bd41f50ba5 | ||
|
|
a2038ab07e | ||
|
|
6445a7674b | ||
|
|
3bf2e89a1a | ||
|
|
e6a13bf386 | ||
|
|
42b2adc03e | ||
|
|
f2b9af01e8 | ||
|
|
9bd2d92755 | ||
|
|
babe884b7c | ||
|
|
200287254b | ||
|
|
0b797f5964 | ||
|
|
13a8c309f5 | ||
|
|
528d2f29d3 | ||
|
|
0aba5dc8de | ||
|
|
72fcf5ab85 | ||
|
|
dd9df068b0 | ||
|
|
81772fb703 | ||
|
|
6304566603 | ||
|
|
5f8b60a0e5 | ||
|
|
4964e075df | ||
|
|
e2accaf70e | ||
|
|
7b32bbfc26 | ||
|
|
6963f3880d | ||
|
|
6c3430b6e5 | ||
|
|
c53f36a777 | ||
|
|
bb4d378733 | ||
|
|
333090a967 | ||
|
|
c8f5646d53 | ||
|
|
3002f6dfd5 | ||
|
|
3f8a50e2ae | ||
|
|
1a504118e5 | ||
|
|
2e602ef6b0 | ||
|
|
d55c3faaeb | ||
|
|
6b185d20c0 | ||
|
|
1bbc33a0cf | ||
|
|
a71eba07a1 | ||
|
|
422dd1fa4f | ||
|
|
c4b2e5829e | ||
|
|
1be222f6ed | ||
|
|
5463f459e6 | ||
|
|
14d7bfdab2 | ||
|
|
ea722da3de | ||
|
|
9aed1e2d17 | ||
|
|
63c6ed3fd0 | ||
|
|
744bfc1982 | ||
|
|
d04434e3ec | ||
|
|
dc3b7b5775 | ||
|
|
17fbfd14db | ||
|
|
d0f5aece5f | ||
|
|
eeda3062e1 | ||
|
|
168d712dec | ||
|
|
e64ad5176e | ||
|
|
0e1469f359 | ||
|
|
7d2b6cfeaf | ||
|
|
8e7d029946 | ||
|
|
9dd62ae0f8 | ||
|
|
e7dff4756f | ||
|
|
fa98d72f3a | ||
|
|
ccf0492890 | ||
|
|
eb0ef6bd3d | ||
|
|
19aa2ccf4d | ||
|
|
841b762796 | ||
|
|
63fb90806c | ||
|
|
88ada80686 | ||
|
|
7d5ddf5e6a | ||
|
|
8f01919f62 | ||
|
|
98394f99b5 | ||
|
|
cae50e16a7 | ||
|
|
4a2ac7bd2e | ||
|
|
a3bdaa85f2 | ||
|
|
40ef86f475 | ||
|
|
4cee14f3c5 | ||
|
|
48d9a8c180 | ||
|
|
1d384e3192 | ||
|
|
87f3dc4558 | ||
|
|
8afec596aa | ||
|
|
26e3263aec | ||
|
|
08b4e1a744 | ||
|
|
d68f0999a4 | ||
|
|
2c9ed4c565 | ||
|
|
be4f87c760 | ||
|
|
b963dadc14 | ||
|
|
26e7fd8b80 | ||
|
|
b9a972bccd | ||
|
|
bb7b9280d3 | ||
|
|
395fbbfd14 | ||
|
|
896dfe3def | ||
|
|
6c4f33910c | ||
|
|
0a301cdd21 | ||
|
|
ce59fc6c10 | ||
|
|
cbb74c984f | ||
|
|
287623df58 | ||
|
|
f8c1d97a25 | ||
|
|
2a188905da | ||
|
|
c2b1e38d7f | ||
|
|
fcc0aef7f4 | ||
|
|
eaa3de2dce | ||
|
|
f3dc5dd12f | ||
|
|
d2f0178fab | ||
|
|
326c386b2e | ||
|
|
6e68c4e2d6 | ||
|
|
a79e96802f | ||
|
|
65b22b493c | ||
|
|
b73f5a4e94 | ||
|
|
3b74ac841e | ||
|
|
253bf776b5 | ||
|
|
eef4acd07d | ||
|
|
b6f00ea241 | ||
|
|
0bd4a4f98f | ||
|
|
0b52645bb6 | ||
|
|
8e845d9f21 | ||
|
|
d29aa43ba4 | ||
|
|
450efea191 | ||
|
|
241cfc4342 | ||
|
|
7c67e3d7e2 | ||
|
|
674790a511 | ||
|
|
4e3c82e329 | ||
|
|
df711b0ea2 | ||
|
|
1019fd9a9d | ||
|
|
8a420dd853 | ||
|
|
f8bcfeb2ab | ||
|
|
34e5beda6a | ||
|
|
6185244754 | ||
|
|
60d9509e39 | ||
|
|
ded4469efe | ||
|
|
1f95d8eedf | ||
|
|
aa66dfff57 | ||
|
|
25263ce40f | ||
|
|
e85deb54e1 | ||
|
|
4750fd159e | ||
|
|
7eea9533e8 | ||
|
|
ec675b9ad2 | ||
|
|
486e77f474 | ||
|
|
048059ba1f | ||
|
|
ed3dda7da9 | ||
|
|
fa93d68b08 | ||
|
|
6093a4f9f8 | ||
|
|
4e20d89d9c | ||
|
|
b420ec6cb9 | ||
|
|
375f6101e9 | ||
|
|
2844d73dc7 | ||
|
|
6c1176f853 | ||
|
|
df037db0bb | ||
|
|
949cc7d21b | ||
|
|
9244529007 | ||
|
|
319d49ddbe | ||
|
|
96fcfdb6c6 | ||
|
|
d61ef6b49a | ||
|
|
804a6c8d47 | ||
|
|
c487cd6af2 | ||
|
|
6a2592a9d9 | ||
|
|
0f48b15695 | ||
|
|
4320b8a5a5 | ||
|
|
a20707cd73 | ||
|
|
5da1d3b73b | ||
|
|
be15e63d41 | ||
|
|
9bc5f686eb | ||
|
|
5bed21dace | ||
|
|
a97e651582 | ||
|
|
dff641a665 | ||
|
|
47a25cc3e8 | ||
|
|
5e3a5f627a | ||
|
|
9201e0a5b9 | ||
|
|
4c80ed3208 | ||
|
|
f34579e921 | ||
|
|
cc1d3b20b6 | ||
|
|
8c56356459 | ||
|
|
21f728f0ea | ||
|
|
8051b6e8b6 | ||
|
|
4c38fec3b5 | ||
|
|
c4cdcf44c5 | ||
|
|
347dab0c14 | ||
|
|
a3d3ea2b4b | ||
|
|
5332387125 | ||
|
|
5b21cbe0de | ||
|
|
2f4111a2e2 | ||
|
|
326ac485b3 | ||
|
|
f9b419d1e4 | ||
|
|
f4e81953ce | ||
|
|
2b09253961 | ||
|
|
1994c6828e | ||
|
|
f5c381d5b4 | ||
|
|
7e2af8364f | ||
|
|
0a2ab2aed2 | ||
|
|
2310a9bbc0 | ||
|
|
5b3e3d9cf4 | ||
|
|
c97c79ab2f | ||
|
|
1231b71245 | ||
|
|
824ffa24f4 | ||
|
|
148f869bec | ||
|
|
c140fe9bae | ||
|
|
4b02ee5b46 | ||
|
|
de3bac53bf | ||
|
|
3f1a76d9e4 | ||
|
|
0138e167e9 | ||
|
|
bcd2ee6204 | ||
|
|
4c1d521711 | ||
|
|
90c70fa5bf | ||
|
|
8e15c48092 | ||
|
|
9cf65e31cd | ||
|
|
a6e5876d96 | ||
|
|
937e723036 | ||
|
|
3a439063a6 | ||
|
|
12d876a005 | ||
|
|
4e2426a2b4 | ||
|
|
020f9cd2a6 | ||
|
|
d7eebe9df0 | ||
|
|
cebc5bf9fc | ||
|
|
dbe68684a0 | ||
|
|
27a05ff271 | ||
|
|
1489ddc49a | ||
|
|
9be2c1beb9 | ||
|
|
9964e6eba3 | ||
|
|
8bcc19d91e | ||
|
|
3c07f57aad | ||
|
|
3262a916e0 | ||
|
|
7883cc5891 | ||
|
|
ded7a5438c | ||
|
|
cd98951001 | ||
|
|
8470c60e06 | ||
|
|
394b1002b3 | ||
|
|
9a61d6293d | ||
|
|
192ad27f8f | ||
|
|
eb0fc67461 | ||
|
|
adbe5e9048 | ||
|
|
fcdf41ba29 | ||
|
|
7b40cbe8c1 | ||
|
|
0933929cfe | ||
|
|
7f618e7ecc | ||
|
|
482cb73702 | ||
|
|
fd1598017a | ||
|
|
96f79475f1 | ||
|
|
3576754c21 | ||
|
|
6c3a0bc72c | ||
|
|
49d75a0cd4 | ||
|
|
756dbac39f | ||
|
|
7df062b7d7 | ||
|
|
e485e8f60d | ||
|
|
fde8ea081a | ||
|
|
f7217c5f26 | ||
|
|
fd77e463a1 | ||
|
|
6d7f6750e9 | ||
|
|
695482ded7 | ||
|
|
fc9649dbc4 | ||
|
|
afb67d375f | ||
|
|
66e38ae69e | ||
|
|
1f4e64f81d | ||
|
|
ad541f713d | ||
|
|
f3b434397b | ||
|
|
1d5d49312c | ||
|
|
232c7361a9 | ||
|
|
721543653b | ||
|
|
d8ba26e664 | ||
|
|
7ecabeac97 | ||
|
|
6cf7be4b7e | ||
|
|
5f2e56674c | ||
|
|
58d4c74b0b | ||
|
|
e137792efd | ||
|
|
b7b934913e | ||
|
|
cd8fcbf9c6 | ||
|
|
d0300d4443 | ||
|
|
40acd9a4c3 | ||
|
|
e66b4d5390 | ||
|
|
f94433e504 | ||
|
|
7f32488b78 | ||
|
|
8f73e24175 | ||
|
|
f3c984281c | ||
|
|
00777a10ae | ||
|
|
0d6ce9f977 | ||
|
|
997c517ba2 | ||
|
|
849bed4bef | ||
|
|
61a48a5b9f | ||
|
|
df14085ec8 | ||
|
|
00b34eb2a4 | ||
|
|
84a6730b1a | ||
|
|
357b514bc9 | ||
|
|
d39b108274 | ||
|
|
39f3239682 | ||
|
|
506f36b26d | ||
|
|
4cedbf80df | ||
|
|
b54ce31078 | ||
|
|
9b2aa974ba | ||
|
|
291c97dc81 | ||
|
|
bcbecff6f6 | ||
|
|
3d22708f67 | ||
|
|
8afd31902f | ||
|
|
89f66ebf6d | ||
|
|
d1a2208196 | ||
|
|
42d1fe5422 | ||
|
|
1424e8a2de | ||
|
|
7dc548b4b8 | ||
|
|
2c37d94611 | ||
|
|
b1e4a7c615 | ||
|
|
b0561058c6 | ||
|
|
d039295070 | ||
|
|
4ddafb8e84 | ||
|
|
a536231ded | ||
|
|
13fe54c938 | ||
|
|
d54ffdd187 | ||
|
|
5c539af7d7 | ||
|
|
47d9a9cf20 | ||
|
|
7f25205aeb | ||
|
|
688973fa44 | ||
|
|
400661d432 | ||
|
|
7ebecf3851 | ||
|
|
0a5eaec0f2 | ||
|
|
4bdab73dd5 | ||
|
|
f19f21007c | ||
|
|
e4e60ed654 | ||
|
|
f21dd9117d | ||
|
|
2aa75f034f | ||
|
|
5c78e0a462 | ||
|
|
52cdedcba0 | ||
|
|
c87cd0de73 | ||
|
|
12530655df | ||
|
|
7479706b29 | ||
|
|
070a141601 | ||
|
|
7044236824 | ||
|
|
3e9478b58d | ||
|
|
5ffca2d138 | ||
|
|
a8d4a98621 | ||
|
|
905f7f4ecc | ||
|
|
819d2bc560 | ||
|
|
c0b2027588 | ||
|
|
136d1b04b5 | ||
|
|
f063dd195e | ||
|
|
be972fc0b5 | ||
|
|
ff90a5d321 | ||
|
|
ac11ba3d60 | ||
|
|
3d8598654c | ||
|
|
55a5da2102 | ||
|
|
f881d6c44f | ||
|
|
5df2ca3ef3 | ||
|
|
a15f87ae39 | ||
|
|
0dd6377fe6 | ||
|
|
c7257e0a3c | ||
|
|
3b7fbcd0c3 | ||
|
|
5fe91af6c3 | ||
|
|
4dd646a424 | ||
|
|
dcbd90ce04 | ||
|
|
04683338a2 | ||
|
|
b778f9c40e | ||
|
|
e0a96be378 | ||
|
|
ac690fceaf | ||
|
|
1e5e03cc46 | ||
|
|
d3c4cd8270 | ||
|
|
17361df66b | ||
|
|
7d0452c7e3 | ||
|
|
cee0ab87fc | ||
|
|
dd0b0cae93 | ||
|
|
c4bf5eef73 | ||
|
|
2587639914 | ||
|
|
bfa1ae59cc | ||
|
|
9e8575c315 | ||
|
|
e36340ce64 | ||
|
|
63ec05a66c | ||
|
|
6f1c72f5b4 | ||
|
|
27924ffd5b | ||
|
|
527029574c | ||
|
|
cf3aeafcdb | ||
|
|
f08a79d372 | ||
|
|
eb2a26cfaa | ||
|
|
5378d9ca26 | ||
|
|
bce11af09a | ||
|
|
158abf5c6c | ||
|
|
b30c1daf72 | ||
|
|
0649206796 | ||
|
|
f589a1d245 | ||
|
|
8a604bd2a1 | ||
|
|
68eb627d62 | ||
|
|
9c87a5890d | ||
|
|
b8a8e2280d | ||
|
|
e538a13e28 | ||
|
|
6fb2a1ed39 | ||
|
|
6b00787f45 | ||
|
|
e735d8d4e5 | ||
|
|
8cd4fd0b50 | ||
|
|
8f4b0559ce | ||
|
|
5288c54aad | ||
|
|
51f8bec81b | ||
|
|
49d3e5d324 | ||
|
|
1fff0e5592 | ||
|
|
8d53ec5353 | ||
|
|
a1e1bfc71b | ||
|
|
29992f54a3 | ||
|
|
4ab6786163 | ||
|
|
eaad34a69a | ||
|
|
839f18d052 | ||
|
|
58ef6d8385 | ||
|
|
a0037c9333 | ||
|
|
177b57e1c0 | ||
|
|
810c129ca9 | ||
|
|
f731a4c704 | ||
|
|
39a1f1ef64 | ||
|
|
d24a87caf1 | ||
|
|
9683ffe13a | ||
|
|
fab2d9dc6a | ||
|
|
59f7a2f6ef | ||
|
|
9b12407028 | ||
|
|
96c4bb7fd0 | ||
|
|
81532f375e | ||
|
|
79eb8e2b35 | ||
|
|
7c2e875494 | ||
|
|
c715b4637d | ||
|
|
c719a61ea7 | ||
|
|
58e4d337e4 | ||
|
|
509c802045 | ||
|
|
4fd63f4e30 | ||
|
|
f5c28c72fd | ||
|
|
342128a457 | ||
|
|
b1931828e1 | ||
|
|
4410226db1 | ||
|
|
945bd4b1b0 | ||
|
|
77f1ea40cd | ||
|
|
98c8c7ce0d | ||
|
|
795bf9e101 | ||
|
|
851fedf751 | ||
|
|
5be6ab8c89 | ||
|
|
3670412c7c | ||
|
|
e6cd596dc9 | ||
|
|
abf4278d09 | ||
|
|
2b2b65fe18 | ||
|
|
3d6a125bdc | ||
|
|
9efd40a366 | ||
|
|
383fa8401d | ||
|
|
bf8ffade29 | ||
|
|
1633d14547 | ||
|
|
e55605dbe9 | ||
|
|
cdec38ba12 | ||
|
|
1cb6e9e7d0 | ||
|
|
d1d2f6f451 | ||
|
|
743f821f1e | ||
|
|
b95a99e0c2 | ||
|
|
3ebbeb103c | ||
|
|
0d4035e996 | ||
|
|
88ed5e506a | ||
|
|
76a3371b40 | ||
|
|
7b2fa1edb4 | ||
|
|
1965035166 | ||
|
|
76d4ff056a | ||
|
|
a239a9efd5 | ||
|
|
4747e7c5b9 | ||
|
|
35ca729cb9 | ||
|
|
a0fa7421d1 | ||
|
|
ddf293bbcd | ||
|
|
45d6e00ff1 | ||
|
|
6dfc8fe0ea | ||
|
|
96f106d6aa | ||
|
|
044da37c95 | ||
|
|
e0b00ee11a | ||
|
|
086444c73a | ||
|
|
1efb2085e9 | ||
|
|
e1c42eb6cc | ||
|
|
a2da26cbdd | ||
|
|
8796adfd63 | ||
|
|
95a6c28d98 | ||
|
|
85e1f4ea13 | ||
|
|
d9c9114b3b | ||
|
|
02140ce763 | ||
|
|
af1cc3b331 | ||
|
|
e852044b64 | ||
|
|
f845b371ce | ||
|
|
3a1bd3114b | ||
|
|
a6d2e3a1e6 | ||
|
|
93bce1b24c | ||
|
|
7b87f29c9c | ||
|
|
cb6f622957 | ||
|
|
43d3b51bde | ||
|
|
57e015155a | ||
|
|
2436d7e0ba | ||
|
|
e6e85b0c55 | ||
|
|
e43fd39594 | ||
|
|
52765466c1 | ||
|
|
fa9afb0860 | ||
|
|
e02bede4f5 | ||
|
|
113089be5d | ||
|
|
712b895170 | ||
|
|
dca163f54d | ||
|
|
fc6cf4d963 | ||
|
|
4126c7e188 | ||
|
|
db50462920 | ||
|
|
9310b44cef | ||
|
|
6480250221 | ||
|
|
710ce7c2e9 | ||
|
|
b88e64f0d0 | ||
|
|
c70432996a | ||
|
|
c6dcf2a0e2 | ||
|
|
2aec627503 | ||
|
|
b7924ce58b | ||
|
|
0bc745f68f | ||
|
|
8acdf823a2 | ||
|
|
52f8b787c9 | ||
|
|
ad153ae041 | ||
|
|
2527f8f599 | ||
|
|
72af092cc1 | ||
|
|
2751060b91 | ||
|
|
b3449db2f8 | ||
|
|
7db28745c8 | ||
|
|
6921211461 | ||
|
|
9f90618a70 | ||
|
|
302c41edc9 | ||
|
|
5d943a35f8 | ||
|
|
03f8d6e946 | ||
|
|
68a35155e4 | ||
|
|
9d725af602 | ||
|
|
d04ccb7a3f | ||
|
|
76c1ed6628 | ||
|
|
6f4abe95cb | ||
|
|
e35e313240 | ||
|
|
3a77a6eded | ||
|
|
0a7a1b9bfb | ||
|
|
3e1418d662 | ||
|
|
884f70fb39 | ||
|
|
9499a1142b | ||
|
|
6e8dcdce78 | ||
|
|
ce9fae82bd | ||
|
|
afa3fc8bf9 | ||
|
|
09eccf6fc0 | ||
|
|
edfefb6763 | ||
|
|
98b3dcbf37 | ||
|
|
2698ef6c5f | ||
|
|
46b2ee3bae | ||
|
|
e7ee3a7dd5 | ||
|
|
0085e6f83b | ||
|
|
c90fa3bcfc | ||
|
|
0ec9b9823f | ||
|
|
50d7b8452d | ||
|
|
d88f8e862b | ||
|
|
fd72cced13 | ||
|
|
18a90734d9 | ||
|
|
a4b2cebef6 | ||
|
|
68d708e56d | ||
|
|
e925ab0999 | ||
|
|
a6feb0a887 | ||
|
|
cd9c3a79e5 | ||
|
|
f3685bdef9 | ||
|
|
e2edf20833 | ||
|
|
31e39f1f6d | ||
|
|
d47c5eaf60 | ||
|
|
b8e515a738 | ||
|
|
0984585d58 | ||
|
|
f3e0eb705b | ||
|
|
04e0f87c03 | ||
|
|
8ccfcd272f | ||
|
|
ba69062a36 | ||
|
|
cea24b5f70 | ||
|
|
f3a71f62a1 | ||
|
|
4e4a6d8397 | ||
|
|
d8beaf727f | ||
|
|
c961c865ee | ||
|
|
34f25fa590 | ||
|
|
7af1155c11 | ||
|
|
c9d7daab70 | ||
|
|
e3feac3fd8 | ||
|
|
1a5989350f | ||
|
|
3365df7778 | ||
|
|
3be5a68e12 | ||
|
|
f158caa2eb | ||
|
|
e02c94eb00 | ||
|
|
981077516b | ||
|
|
7c488b5913 | ||
|
|
0e44f587a5 | ||
|
|
e593d86b80 | ||
|
|
339a8ad610 | ||
|
|
d1190febec | ||
|
|
5d2c5b01a8 | ||
|
|
0f954c20ff | ||
|
|
d6edff3182 | ||
|
|
dba26c3240 | ||
|
|
0ca5b7996c | ||
|
|
9507b121ac | ||
|
|
3812b275e9 | ||
|
|
6c1561f415 | ||
|
|
9ab6353d73 | ||
|
|
ff60dc4d24 | ||
|
|
62e7d904b4 | ||
|
|
5d3de4b670 | ||
|
|
7d75ad4c56 | ||
|
|
63e1d3610c | ||
|
|
b4325026b1 | ||
|
|
b072ef2e82 | ||
|
|
562a4c056e | ||
|
|
bb8248556d | ||
|
|
b22b085b50 | ||
|
|
e82ea94bb6 | ||
|
|
a50252382a | ||
|
|
9bdb799b41 | ||
|
|
989651c23b | ||
|
|
259cefab72 | ||
|
|
09ed421948 | ||
|
|
87f5ec5be5 | ||
|
|
f78b656f5f | ||
|
|
e6b940e247 | ||
|
|
50a9680f17 | ||
|
|
f2a6dc4dfd | ||
|
|
67e3dd36b3 | ||
|
|
6c4cc357b5 | ||
|
|
94965a418f | ||
|
|
40e6ba1100 | ||
|
|
0aed065a75 | ||
|
|
70b63a5ed4 | ||
|
|
1476a9ecf1 | ||
|
|
600a235140 | ||
|
|
ffa9f104db | ||
|
|
b807ec0956 | ||
|
|
5f8daeeb6d | ||
|
|
aec8307417 | ||
|
|
e96a1b01b9 | ||
|
|
d78ba322bf | ||
|
|
ab5c1b0a3a | ||
|
|
53fa16d39f | ||
|
|
05cf405cb5 | ||
|
|
5d833336d3 | ||
|
|
13be9d8d79 | ||
|
|
bb6326f4d4 | ||
|
|
c070407ab2 | ||
|
|
6b27bdadcd | ||
|
|
1c22c2f76a | ||
|
|
aa262d4124 | ||
|
|
646c0bfcb9 | ||
|
|
da68775472 | ||
|
|
bcf96608d1 | ||
|
|
dd17ac5045 | ||
|
|
59182dbc97 | ||
|
|
11df7187b3 | ||
|
|
0899803294 | ||
|
|
4742e0951c | ||
|
|
c3b1eb0837 | ||
|
|
542d7977db | ||
|
|
11927a768e | ||
|
|
ecd5bcec0c | ||
|
|
911a2084d4 | ||
|
|
839bf0e2c9 | ||
|
|
25555b8c3e | ||
|
|
cd7ac94f35 | ||
|
|
68aea3af9e | ||
|
|
cdb3216cc9 | ||
|
|
78f0201dfa | ||
|
|
2cd47b0240 | ||
|
|
be83a6a37a | ||
|
|
8ca99e85b0 | ||
|
|
f5b546b3c8 | ||
|
|
50c9d17830 | ||
|
|
d87af4788e | ||
|
|
0f21537f14 | ||
|
|
0533bde9f2 | ||
|
|
32b3717c32 | ||
|
|
d950f801af | ||
|
|
a8ab028b84 | ||
|
|
2aff36e74b | ||
|
|
db7e4bf940 | ||
|
|
ee20015d44 | ||
|
|
80941f8413 | ||
|
|
cf909db159 | ||
|
|
27dbe77fad | ||
|
|
38f2334360 | ||
|
|
6489a48e1f | ||
|
|
ca24d1762e | ||
|
|
2f4b84c074 | ||
|
|
6ae810a1fa | ||
|
|
3e3f695536 | ||
|
|
3ca93f4a4c | ||
|
|
f2d9930773 | ||
|
|
2fbf399156 | ||
|
|
5fe91d6577 | ||
|
|
753d0e7df7 | ||
|
|
e2cc350fbc | ||
|
|
07feb87dee | ||
|
|
1699e94f0f | ||
|
|
8a1e335bf5 | ||
|
|
5413bf8753 | ||
|
|
c4c5ecd03d | ||
|
|
3b67cf4378 | ||
|
|
f3c937f9ed | ||
|
|
671a699472 | ||
|
|
cc4fa1c6bd | ||
|
|
d11d476126 | ||
|
|
1ce06c7cdd | ||
|
|
800f02ba38 | ||
|
|
79964b0e85 | ||
|
|
0924d11bbf | ||
|
|
3cf85634eb | ||
|
|
ec01175d37 | ||
|
|
5bb518ff0f | ||
|
|
5415381cf4 | ||
|
|
129ae06354 | ||
|
|
058e5d5f4b | ||
|
|
2ec493f033 | ||
|
|
a33e8a8509 | ||
|
|
7b4be7be40 | ||
|
|
b90917a529 | ||
|
|
180f05f6f0 | ||
|
|
b2686e5b6d | ||
|
|
831b95f13a | ||
|
|
331b599a81 | ||
|
|
3ef817ebc2 | ||
|
|
d0f7c309ab | ||
|
|
e5079b9dad | ||
|
|
9e773c9143 | ||
|
|
fe600441c9 | ||
|
|
291b56db93 | ||
|
|
ec9975c3d9 | ||
|
|
9e57161941 | ||
|
|
20444bf253 | ||
|
|
e428c4402e | ||
|
|
15af89d51c | ||
|
|
ed95865696 | ||
|
|
8c71bd57e7 | ||
|
|
d204be4b2c | ||
|
|
4743171b4f | ||
|
|
1fb800f7a7 | ||
|
|
3a3b0dd5c1 | ||
|
|
2959281d42 | ||
|
|
e2c939fb97 | ||
|
|
51d0e4325e | ||
|
|
08b6cf0231 | ||
|
|
059147b74a | ||
|
|
c96ceeb1bd | ||
|
|
0a3b6c4813 | ||
|
|
8636a15695 | ||
|
|
96db9362c5 | ||
|
|
f81f93e9c3 | ||
|
|
a3a04b5f76 | ||
|
|
329174b6d9 | ||
|
|
b171aff418 | ||
|
|
1f6ffa3e88 | ||
|
|
24d2a8b9d5 | ||
|
|
4d8b99a307 | ||
|
|
f9085fedd7 | ||
|
|
c4236e58d1 | ||
|
|
eb91f9575a | ||
|
|
947d14ffeb | ||
|
|
b3f915b5b2 | ||
|
|
03d3f3afc3 | ||
|
|
2728170c01 | ||
|
|
c107f3632a | ||
|
|
7d1c5fca0b | ||
|
|
e71887e1a8 | ||
|
|
39d1eeda23 | ||
|
|
b482f3ce94 | ||
|
|
122cc48c29 | ||
|
|
c7c57cfa0e | ||
|
|
1f9ca20dfd | ||
|
|
5efbaa4849 | ||
|
|
c6f55bc4ab | ||
|
|
b28a3db3d6 | ||
|
|
b9ece28f68 | ||
|
|
79b4907cb9 | ||
|
|
dfbc244b00 | ||
|
|
b23d5e26e2 | ||
|
|
f1f3074306 | ||
|
|
f162ad193f | ||
|
|
5254f300f9 | ||
|
|
c0d0100ca8 | ||
|
|
21f201e371 | ||
|
|
bbc378ed8f | ||
|
|
e2d494321c | ||
|
|
c2df8043db | ||
|
|
df62150b5a | ||
|
|
eea52a5fa6 | ||
|
|
2d9015b840 | ||
|
|
187ef29914 | ||
|
|
c12be766e9 | ||
|
|
2168458f11 | ||
|
|
8cf0725593 | ||
|
|
8379f015d7 | ||
|
|
f08c01800f | ||
|
|
a62706678e | ||
|
|
e4468562d2 | ||
|
|
7316bf7bc5 | ||
|
|
3747b7d930 | ||
|
|
5be3f22d06 | ||
|
|
c572ce946b | ||
|
|
d69f0289ca | ||
|
|
048e5210f7 | ||
|
|
219e9115c0 | ||
|
|
b43416af97 | ||
|
|
6d84da588b | ||
|
|
569d6c557c | ||
|
|
58bb94d7c7 | ||
|
|
4f8f775e69 | ||
|
|
5eed02f7e9 | ||
|
|
983f1f28ca | ||
|
|
192ede5e64 | ||
|
|
803a7aa878 | ||
|
|
93f3098aec | ||
|
|
dbd94d047b | ||
|
|
be39ab32d1 | ||
|
|
8b1fb3cb0c | ||
|
|
9e04222ee6 | ||
|
|
72349507c4 | ||
|
|
79db8daddd | ||
|
|
b967f83f20 | ||
|
|
74a7592b4b | ||
|
|
486f23538f | ||
|
|
df86ff2191 | ||
|
|
41266f05e9 | ||
|
|
d2aa331838 | ||
|
|
670cb9d223 | ||
|
|
a205762bf0 | ||
|
|
2ffab66d97 | ||
|
|
10a6aec998 | ||
|
|
7cbe31baad | ||
|
|
8a09dc1b9b | ||
|
|
fa6e174651 | ||
|
|
8dee328eae | ||
|
|
340155e6a6 | ||
|
|
7fe19a030a | ||
|
|
11e0ccdc91 | ||
|
|
e55b2f4f8d | ||
|
|
d78c1f695e | ||
|
|
cfbb3e86b3 | ||
|
|
fa6234e417 | ||
|
|
9eeae9ad7e | ||
|
|
28688488ff | ||
|
|
41d804719f | ||
|
|
bc18168662 | ||
|
|
1fadae82c7 | ||
|
|
791c62ca64 | ||
|
|
4441a6ff59 | ||
|
|
63a7002477 | ||
|
|
a746139c53 | ||
|
|
697a060132 | ||
|
|
7834c25253 | ||
|
|
2e7cbfcff5 | ||
|
|
5b771039fc | ||
|
|
2f1bc5864f | ||
|
|
16d79ebaac | ||
|
|
e009ec8b93 | ||
|
|
738ece513f | ||
|
|
5a7b7b51c5 | ||
|
|
5bba9bddfd | ||
|
|
a3c0c7546d | ||
|
|
05d7ae4efa | ||
|
|
44483dba21 | ||
|
|
3498a5856a | ||
|
|
cd79b0343a | ||
|
|
abba00d595 | ||
|
|
88a8d7d901 | ||
|
|
d5c00071d3 | ||
|
|
c7a6cf5463 | ||
|
|
2ce2a15fc6 | ||
|
|
ce4be4e91e | ||
|
|
82dc2244c0 | ||
|
|
ab45b7783f | ||
|
|
4a56b2406b | ||
|
|
e0cd2cdb9c | ||
|
|
7575094cf3 | ||
|
|
40a3ae04b6 | ||
|
|
ac26f84170 | ||
|
|
fa574fe833 | ||
|
|
9a6e18ce80 | ||
|
|
8f9034fc8b | ||
|
|
3e5b102445 | ||
|
|
d8b1a8d439 | ||
|
|
54ae008dd7 | ||
|
|
9c1747581f | ||
|
|
ae4f7b6d7a | ||
|
|
c947322a69 | ||
|
|
14db45215f | ||
|
|
d20831e41a | ||
|
|
1bb902984b | ||
|
|
09f1c58872 | ||
|
|
7f944c2c8b | ||
|
|
1c02b85802 | ||
|
|
a2801649b4 | ||
|
|
15777732d3 | ||
|
|
b875037150 | ||
|
|
a6d583cb5e | ||
|
|
9882f3429a | ||
|
|
29d47c4de2 | ||
|
|
a9b15f1c36 | ||
|
|
ce38ecb966 | ||
|
|
13ffa17048 | ||
|
|
a387682dfb | ||
|
|
f6ed197cd3 | ||
|
|
243593cdaa | ||
|
|
1e2d559859 | ||
|
|
20ea859183 | ||
|
|
86b24ea059 | ||
|
|
07af42476d | ||
|
|
87edf71e93 | ||
|
|
e3ea2ed420 | ||
|
|
8f9a1881a4 | ||
|
|
a8c6111197 | ||
|
|
3de8570022 | ||
|
|
a0311b0134 | ||
|
|
158a628c0e | ||
|
|
422e5026d6 | ||
|
|
797cbb9b20 | ||
|
|
be68fbd4f5 | ||
|
|
e440223b40 | ||
|
|
5766250288 | ||
|
|
870274ad9d | ||
|
|
9d6abcd9be | ||
|
|
432771dfe3 | ||
|
|
69925ce823 | ||
|
|
e9f9f515bd | ||
|
|
efd96153d8 | ||
|
|
c7b8f223ee | ||
|
|
a0636d5a87 | ||
|
|
c7b16249b8 | ||
|
|
031e885e4d | ||
|
|
796e2cc156 | ||
|
|
7af784adce | ||
|
|
a988a91e2e | ||
|
|
a8b564fa64 | ||
|
|
b97e140389 | ||
|
|
e51bef6d12 | ||
|
|
4bd31f4967 | ||
|
|
7ff7a7c527 | ||
|
|
c4a375b3a5 | ||
|
|
e3698edd19 | ||
|
|
e591d5cfe4 | ||
|
|
a2e62f8e1d | ||
|
|
764963e986 | ||
|
|
4da493f3f3 | ||
|
|
dec90f7e5e | ||
|
|
2ea5b283a8 | ||
|
|
eb23549cd6 | ||
|
|
cd90062850 | ||
|
|
3ec72fcee9 | ||
|
|
b9091e14b3 | ||
|
|
5daefc0194 | ||
|
|
44edb2bd2f | ||
|
|
201aa24448 | ||
|
|
e799ef2977 | ||
|
|
0c538f7527 | ||
|
|
ca7202eb0a | ||
|
|
d5ec5f80ff | ||
|
|
0c944a03fe | ||
|
|
79a267ab08 | ||
|
|
f9a6988ece | ||
|
|
95e06de5de | ||
|
|
f530a5074b | ||
|
|
439580b91b | ||
|
|
4c2a384159 | ||
|
|
22b83d7630 | ||
|
|
cfd0b040e4 | ||
|
|
b001840dee | ||
|
|
0b5bff01e1 | ||
|
|
4fe7b6cd65 | ||
|
|
49e1f7d8bf | ||
|
|
803fb243bf | ||
|
|
20a6ab3d1a | ||
|
|
21f16b50f3 | ||
|
|
f58e83ee87 | ||
|
|
a9b4a2a1ac | ||
|
|
5be1449db5 | ||
|
|
37d7f89c6d | ||
|
|
57e58ce76c | ||
|
|
1d9f76e2c8 | ||
|
|
56e0269e5e | ||
|
|
662df85e54 | ||
|
|
2be4a5e486 | ||
|
|
a43d697225 | ||
|
|
f06c1e6c78 | ||
|
|
b5d1918401 | ||
|
|
d018be5d36 | ||
|
|
67184d7b20 | ||
|
|
dfdd48b990 | ||
|
|
cae9cee295 | ||
|
|
067d586c1c | ||
|
|
d22b7938da | ||
|
|
29b7510957 | ||
|
|
9d548d81ac | ||
|
|
3c33cdfa3d | ||
|
|
288485b209 | ||
|
|
1cbf416b10 | ||
|
|
3b46d3dd74 | ||
|
|
c243829234 | ||
|
|
fd80faa389 | ||
|
|
e7d4352292 | ||
|
|
610e0f21d6 | ||
|
|
7e512bad96 | ||
|
|
98c4ce0c45 | ||
|
|
93fc48a2db | ||
|
|
d7eea12a6f | ||
|
|
2a1e06f8a9 | ||
|
|
02d54a783a | ||
|
|
5961d44339 | ||
|
|
fb3be8509d | ||
|
|
97be53741d | ||
|
|
0d2c26735e | ||
|
|
2d0a8ddb58 | ||
|
|
72518d4827 | ||
|
|
c6cd744186 | ||
|
|
ae2db62f1c | ||
|
|
8c76b8bc36 | ||
|
|
18256c4923 | ||
|
|
f345cc66cf | ||
|
|
5dbf664a6b | ||
|
|
d7c6679d70 | ||
|
|
cae203be71 | ||
|
|
8a29fbc850 | ||
|
|
00bcbd367f | ||
|
|
1befee5aca | ||
|
|
5982f4bcf0 | ||
|
|
2399476a21 | ||
|
|
9e45ac939b | ||
|
|
34f231f9f3 | ||
|
|
f940b2a58e | ||
|
|
0383c33558 | ||
|
|
3afa4b210d | ||
|
|
998783eb9d | ||
|
|
54d61bdc4a | ||
|
|
2d279c4c5c | ||
|
|
0fb206fe15 | ||
|
|
5d6fd8099f | ||
|
|
800e3f4599 | ||
|
|
35c61f52fe | ||
|
|
3db446633c | ||
|
|
e3b4a0213c | ||
|
|
9910ff5fa1 | ||
|
|
049be10406 | ||
|
|
9aa3be7f9f | ||
|
|
aba5c634ae | ||
|
|
9774b01b0e | ||
|
|
ecf0a710e1 | ||
|
|
c9febbdd87 | ||
|
|
caa2e45a8c | ||
|
|
d9130c9852 | ||
|
|
661f05837c | ||
|
|
a61fe418b2 | ||
|
|
b15cfc2c5a | ||
|
|
c575c9af3d | ||
|
|
81f27e9077 | ||
|
|
f78babfaa0 | ||
|
|
66990cf872 | ||
|
|
095fe2ed1b | ||
|
|
483ebc8141 | ||
|
|
b9311282eb | ||
|
|
d6f0c2b52b | ||
|
|
bb25febd70 | ||
|
|
19ab2a29ce | ||
|
|
24b4fe9867 | ||
|
|
aa7b82de04 | ||
|
|
3d826bed3a | ||
|
|
d2ae7e78ef | ||
|
|
656bd330f8 | ||
|
|
c2c8f32010 | ||
|
|
9f43c270e6 | ||
|
|
50827188ff | ||
|
|
6ae0f7f5c6 | ||
|
|
c583d6bb49 | ||
|
|
fdcb6b721c | ||
|
|
ef858ef062 | ||
|
|
fe04faf675 | ||
|
|
ad752b317d | ||
|
|
59649e9b1e | ||
|
|
6bf281f905 | ||
|
|
4dfc8b262c | ||
|
|
f08ffe9feb | ||
|
|
65de3110a9 | ||
|
|
f6dcd98995 | ||
|
|
950172dc01 | ||
|
|
5c48e139d4 | ||
|
|
150e9c8a48 | ||
|
|
b19ba13aff | ||
|
|
30bfc2cea7 | ||
|
|
30c2d84c6d | ||
|
|
fbd2038fa7 | ||
|
|
e4b8d9b9d6 | ||
|
|
6b50003697 | ||
|
|
a73c5b3355 | ||
|
|
eb59817e81 | ||
|
|
d404e92d16 | ||
|
|
86c017ec9d | ||
|
|
0407c4e0f7 | ||
|
|
0ba95a3dd4 | ||
|
|
0463b5d6cd | ||
|
|
3f4513b3a9 | ||
|
|
fb2029e717 | ||
|
|
48f02fb61b | ||
|
|
0c00e870c6 | ||
|
|
7da50703fb | ||
|
|
cfdaff5a46 | ||
|
|
18e46962c2 | ||
|
|
319e0ae3cf | ||
|
|
e69a7c38d9 | ||
|
|
ecd685755b | ||
|
|
276b51d927 | ||
|
|
251d1c5c9f | ||
|
|
028e17475f | ||
|
|
1ab630435d | ||
|
|
89002ed298 | ||
|
|
d0871bdae3 | ||
|
|
5ea6e9c9c0 | ||
|
|
267f283a31 | ||
|
|
3324c0ae79 | ||
|
|
811bff6db0 | ||
|
|
0c9546ccb0 | ||
|
|
7b16442656 | ||
|
|
b0070f03af | ||
|
|
08ee072f11 | ||
|
|
f3e4cea34f | ||
|
|
2d12b68952 | ||
|
|
1643b476eb | ||
|
|
5980ebc79a | ||
|
|
8371b030cf | ||
|
|
10afcaca2f | ||
|
|
2fb4b62330 | ||
|
|
cbcd7e0f86 | ||
|
|
df1c9d88a8 | ||
|
|
3aae1ae3d9 | ||
|
|
775bd1abd0 | ||
|
|
78009539d1 | ||
|
|
36246ad9ac | ||
|
|
e2053b22b4 | ||
|
|
dfdc402fbb | ||
|
|
73ba54a502 | ||
|
|
690a5e205d | ||
|
|
a6014bf04e | ||
|
|
d4d1f0f4a9 | ||
|
|
5fbc47eb3a | ||
|
|
6d4e903b08 | ||
|
|
66f08eb236 | ||
|
|
933c169da5 | ||
|
|
a8df88ab91 | ||
|
|
df9547ae39 | ||
|
|
d529eb6d00 | ||
|
|
fac1e367c9 | ||
|
|
31a5487cba | ||
|
|
43822d37a7 | ||
|
|
527dd31c70 | ||
|
|
e3c66532c5 | ||
|
|
22ea4004e1 | ||
|
|
d190a43e0a | ||
|
|
bd5e57d879 | ||
|
|
08928b486b | ||
|
|
75da0713d2 | ||
|
|
d35bf51780 | ||
|
|
ec603beeb0 | ||
|
|
7820467d59 | ||
|
|
fe09757cb3 | ||
|
|
1e6b68f5d1 | ||
|
|
f574e5813f | ||
|
|
30684246d2 | ||
|
|
8bdf5cf854 | ||
|
|
c93ec9331b | ||
|
|
0886e67df7 | ||
|
|
30de13b4df | ||
|
|
7a60c14b31 | ||
|
|
3ad08e9515 | ||
|
|
32fdc19697 | ||
|
|
c9c31c04c3 | ||
|
|
c3dd3ef0d7 | ||
|
|
fa989a554d | ||
|
|
4a4dacb52c | ||
|
|
dcf9cb581d | ||
|
|
1a6305c93f | ||
|
|
bdbf323fee | ||
|
|
7270f277a7 | ||
|
|
32dfc387c8 | ||
|
|
b3752e78b6 | ||
|
|
4c0d3f1b75 | ||
|
|
2e3cb75530 | ||
|
|
3e99ffe692 | ||
|
|
096d8992a1 | ||
|
|
0bbe6eef89 | ||
|
|
d0b748a4f2 | ||
|
|
58f41a194a | ||
|
|
d8d10bc478 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: acmesh
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
27
.github/ISSUE_TEMPLATE.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!--
|
||||||
|
我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海.
|
||||||
|
|
||||||
|
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么.
|
||||||
|
如何调试 https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
|
||||||
|
|
||||||
|
If it is a bug report:
|
||||||
|
- make sure you are able to repro it on the latest released version.
|
||||||
|
You can install the latest version by: `acme.sh --upgrade`
|
||||||
|
|
||||||
|
- Search the existing issues.
|
||||||
|
- Refer to the [WIKI](https://wiki.acme.sh).
|
||||||
|
- Debug info [Debug](https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh).
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
Steps to reproduce
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Debug log
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```
|
||||||
|
acme.sh --issue ..... --debug 2
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!--
|
||||||
|
1. Do NOT send pull request to `master` branch.
|
||||||
|
Please send to `dev` branch instead.
|
||||||
|
Any PR to `master` branch will NOT be merged.
|
||||||
|
|
||||||
|
2. For dns api support, read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
|
||||||
|
You will NOT get any review without passing this guide. You also need to fix the CI errors.
|
||||||
|
|
||||||
|
-->
|
||||||
254
.github/workflows/DNS.yml
vendored
Normal file
254
.github/workflows/DNS.yml
vendored
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
name: DNS
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'dnsapi/*.sh'
|
||||||
|
- '.github/workflows/DNS.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'dev'
|
||||||
|
paths:
|
||||||
|
- 'dnsapi/*.sh'
|
||||||
|
- '.github/workflows/DNS.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CheckToken:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
hasToken: ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
steps:
|
||||||
|
- name: Set the value
|
||||||
|
id: step_one
|
||||||
|
run: |
|
||||||
|
if [ "${{secrets.TokenName1}}" ] ; then
|
||||||
|
echo "::set-output name=hasToken::true"
|
||||||
|
else
|
||||||
|
echo "::set-output name=hasToken::false"
|
||||||
|
fi
|
||||||
|
- name: Check the value
|
||||||
|
run: echo ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
|
||||||
|
Fail:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'false')"
|
||||||
|
steps:
|
||||||
|
- name: "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
|
||||||
|
run: |
|
||||||
|
echo "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
|
||||||
|
if [ "${{github.repository_owner}}" != "acmesh-official" ]; then
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
|
||||||
|
Docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Set env file
|
||||||
|
run: |
|
||||||
|
cd ../acmetest
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env
|
||||||
|
fi
|
||||||
|
echo "TEST_DNS_NO_WILDCARD" >> docker.env
|
||||||
|
echo "TEST_DNS_SLEEP" >> docker.env
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./rundocker.sh testall
|
||||||
|
|
||||||
|
MacOS:
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: Docker
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install tools
|
||||||
|
run: brew install socat
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
needs: MacOS
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: 1
|
||||||
|
steps:
|
||||||
|
- name: Set git to use LF
|
||||||
|
run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install cygwin base packages with chocolatey
|
||||||
|
run: |
|
||||||
|
choco config get cacheLocation
|
||||||
|
choco install --no-progress cygwin
|
||||||
|
shell: cmd
|
||||||
|
- name: Install cygwin additional packages
|
||||||
|
run: |
|
||||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s http://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git
|
||||||
|
shell: cmd
|
||||||
|
- name: Set ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin >> %GITHUB_ENV%
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
FreeBSD:
|
||||||
|
runs-on: macos-10.15
|
||||||
|
needs: Windows
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/freebsd-vm@v0.1.4
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: pkg install -y socat curl
|
||||||
|
usesh: true
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
Solaris:
|
||||||
|
runs-on: macos-10.15
|
||||||
|
needs: FreeBSD
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/solaris-vm@v0.0.5
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: pkgutil -y -i socat
|
||||||
|
run: |
|
||||||
|
pkg set-mediator -v -I default@1.1 openssl
|
||||||
|
export PATH=/usr/gnu/bin:$PATH
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
63
.github/workflows/FreeBSD.yml
vendored
Normal file
63
.github/workflows/FreeBSD.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
name: FreeBSD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/FreeBSD.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/FreeBSD.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
FreeBSD:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: macos-10.15
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: vmactions/cf-tunnel@v0.0.3
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/freebsd-vm@v0.1.5
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg install -y socat curl
|
||||||
|
usesh: true
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
41
.github/workflows/Linux.yml
vendored
Normal file
41
.github/workflows/Linux.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Linux
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Linux.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Linux.yml'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Linux:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "centos:7", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./rundocker.sh testplat ${{ matrix.os }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
55
.github/workflows/MacOS.yml
vendored
Normal file
55
.github/workflows/MacOS.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: MacOS
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/MacOS.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/MacOS.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
MacOS:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: macos-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install tools
|
||||||
|
run: brew install socat
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& sudo --preserve-env ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
62
.github/workflows/PebbleStrict.yml
vendored
Normal file
62
.github/workflows/PebbleStrict.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: PebbleStrict
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/PebbleStrict.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/PebbleStrict.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
PebbleStrict:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TestingDomain: example.com
|
||||||
|
TestingAltDomains: www.example.com
|
||||||
|
TEST_ACME_Server: https://localhost:14000/dir
|
||||||
|
HTTPS_INSECURE: 1
|
||||||
|
Le_HTTPPort: 5002
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_CA: "Pebble Intermediate CA"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat
|
||||||
|
- name: Run Pebble
|
||||||
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d
|
||||||
|
- name: Set up Pebble
|
||||||
|
run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./letest.sh
|
||||||
|
|
||||||
|
PebbleStrict_IPCert:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TestingDomain: 10.30.50.1
|
||||||
|
ACME_DIRECTORY: https://localhost:14000/dir
|
||||||
|
HTTPS_INSECURE: 1
|
||||||
|
Le_HTTPPort: 5002
|
||||||
|
Le_TLSPort: 5001
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_CA: "Pebble Intermediate CA"
|
||||||
|
TEST_IPCERT: 1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat
|
||||||
|
- name: Run Pebble
|
||||||
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./letest.sh
|
||||||
61
.github/workflows/Solaris.yml
vendored
Normal file
61
.github/workflows/Solaris.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Solaris
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Solaris:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: macos-10.15
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: vmactions/cf-tunnel@v0.0.3
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/solaris-vm@v0.0.5
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkgutil -y -i socat curl
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
86
.github/workflows/Ubuntu.yml
vendored
Normal file
86
.github/workflows/Ubuntu.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
name: Ubuntu
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Ubuntu.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Ubuntu.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Ubuntu:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
|
||||||
|
CA_ECDSA: "Smallstep Intermediate CA"
|
||||||
|
CA: "Smallstep Intermediate CA"
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
NO_REVOKE: 1
|
||||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
|
||||||
|
CA_ECDSA: "Smallstep Intermediate CA"
|
||||||
|
CA: "Smallstep Intermediate CA"
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
NO_REVOKE: 1
|
||||||
|
TEST_IPCERT: 1
|
||||||
|
TestingDomain: "172.17.0.1"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
NO_ECC_384: ${{ matrix.NO_ECC_384 }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
NO_REVOKE: ${{ matrix.NO_REVOKE }}
|
||||||
|
TEST_IPCERT: ${{ matrix.TEST_IPCERT }}
|
||||||
|
TestingDomain: ${{ matrix.TestingDomain }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat
|
||||||
|
- name: Start StepCA
|
||||||
|
if: ${{ matrix.TEST_ACME_Server=='https://localhost:9000/acme/acme/directory' }}
|
||||||
|
run: |
|
||||||
|
docker run --rm -d \
|
||||||
|
-p 9000:9000 \
|
||||||
|
-e "DOCKER_STEPCA_INIT_NAME=Smallstep" \
|
||||||
|
-e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \
|
||||||
|
--name stepca \
|
||||||
|
smallstep/step-ca \
|
||||||
|
&& sleep 5 && docker exec stepca step ca provisioner add acme --type ACME \
|
||||||
|
&& docker exec stepca kill -1 1 \
|
||||||
|
&& docker exec stepca cat /home/step/certs/root_ca.crt | sudo bash -c "cat - >>/etc/ssl/certs/ca-certificates.crt"
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& sudo --preserve-env ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
73
.github/workflows/Windows.yml
vendored
Normal file
73
.github/workflows/Windows.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
name: Windows
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Windows.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Windows.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Windows:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: windows-latest
|
||||||
|
env:
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
#The 80 port is used by Windows server, we have to use a custom port, tunnel will also use this port.
|
||||||
|
Le_HTTPPort: 8888
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- name: Set git to use LF
|
||||||
|
run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install cygwin base packages with chocolatey
|
||||||
|
run: |
|
||||||
|
choco config get cacheLocation
|
||||||
|
choco install --no-progress cygwin
|
||||||
|
shell: cmd
|
||||||
|
- name: Install cygwin additional packages
|
||||||
|
run: |
|
||||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s http://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git,xxd
|
||||||
|
shell: cmd
|
||||||
|
- name: Set ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin;%PATH% >> %GITHUB_ENV%
|
||||||
|
- name: Check ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo "PATH=%PATH%"
|
||||||
|
- name: Clone acmetest
|
||||||
|
shell: cmd
|
||||||
|
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
shell: cmd
|
||||||
|
run: cd ../acmetest && bash.exe -c ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
69
.github/workflows/dockerhub.yml
vendored
Normal file
69
.github/workflows/dockerhub.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
name: Build DockerHub
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- "Dockerfile"
|
||||||
|
- '.github/workflows/dockerhub.yml'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CheckToken:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
hasToken: ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
env:
|
||||||
|
DOCKER_PASSWORD : ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
steps:
|
||||||
|
- name: Set the value
|
||||||
|
id: step_one
|
||||||
|
run: |
|
||||||
|
if [ "$DOCKER_PASSWORD" ] ; then
|
||||||
|
echo "::set-output name=hasToken::true"
|
||||||
|
else
|
||||||
|
echo "::set-output name=hasToken::false"
|
||||||
|
fi
|
||||||
|
- name: Check the value
|
||||||
|
run: echo ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
|
steps:
|
||||||
|
- name: checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: login to docker hub
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||||
|
- name: build and push the image
|
||||||
|
run: |
|
||||||
|
DOCKER_IMAGE=neilpang/acme.sh
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/heads/}
|
||||||
|
|
||||||
|
if [[ $DOCKER_IMAGE_TAG == master ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=latest
|
||||||
|
AUTO_UPGRADE=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker buildx build \
|
||||||
|
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
||||||
|
--output "type=image,push=true" \
|
||||||
|
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
||||||
|
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
||||||
33
.github/workflows/shellcheck.yml
vendored
Normal file
33
.github/workflows/shellcheck.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Shellcheck
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- '.github/workflows/shellcheck.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- '.github/workflows/shellcheck.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ShellCheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install Shellcheck
|
||||||
|
run: sudo apt-get install -y shellcheck
|
||||||
|
- name: DoShellcheck
|
||||||
|
run: shellcheck -V && shellcheck -e SC2181 **/*.sh && echo "shellcheck OK"
|
||||||
|
|
||||||
|
shfmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install shfmt
|
||||||
|
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
|
||||||
|
- name: shfmt
|
||||||
|
run: ~/shfmt -l -w -i 2 . ; git diff --exit-code && echo "shfmt OK"
|
||||||
76
Dockerfile
Normal file
76
Dockerfile
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
FROM alpine:3.15
|
||||||
|
|
||||||
|
RUN apk --no-cache add -f \
|
||||||
|
openssl \
|
||||||
|
openssh-client \
|
||||||
|
coreutils \
|
||||||
|
bind-tools \
|
||||||
|
curl \
|
||||||
|
sed \
|
||||||
|
socat \
|
||||||
|
tzdata \
|
||||||
|
oath-toolkit-oathtool \
|
||||||
|
tar \
|
||||||
|
libidn \
|
||||||
|
jq
|
||||||
|
|
||||||
|
ENV LE_CONFIG_HOME /acme.sh
|
||||||
|
|
||||||
|
ARG AUTO_UPGRADE=1
|
||||||
|
|
||||||
|
ENV AUTO_UPGRADE $AUTO_UPGRADE
|
||||||
|
|
||||||
|
#Install
|
||||||
|
COPY ./ /install_acme.sh/
|
||||||
|
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
||||||
|
|
||||||
|
|
||||||
|
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
|
||||||
|
|
||||||
|
RUN for verb in help \
|
||||||
|
version \
|
||||||
|
install \
|
||||||
|
uninstall \
|
||||||
|
upgrade \
|
||||||
|
issue \
|
||||||
|
signcsr \
|
||||||
|
deploy \
|
||||||
|
install-cert \
|
||||||
|
renew \
|
||||||
|
renew-all \
|
||||||
|
revoke \
|
||||||
|
remove \
|
||||||
|
list \
|
||||||
|
info \
|
||||||
|
showcsr \
|
||||||
|
install-cronjob \
|
||||||
|
uninstall-cronjob \
|
||||||
|
cron \
|
||||||
|
toPkcs \
|
||||||
|
toPkcs8 \
|
||||||
|
update-account \
|
||||||
|
register-account \
|
||||||
|
create-account-key \
|
||||||
|
create-domain-key \
|
||||||
|
createCSR \
|
||||||
|
deactivate \
|
||||||
|
deactivate-account \
|
||||||
|
set-notify \
|
||||||
|
set-default-ca \
|
||||||
|
set-default-chain \
|
||||||
|
; do \
|
||||||
|
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
|
||||||
|
; done
|
||||||
|
|
||||||
|
RUN printf "%b" '#!'"/usr/bin/env sh\n \
|
||||||
|
if [ \"\$1\" = \"daemon\" ]; then \n \
|
||||||
|
trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \
|
||||||
|
crond && while true; do sleep 1; done;\n \
|
||||||
|
else \n \
|
||||||
|
exec -- \"\$@\"\n \
|
||||||
|
fi" >/entry.sh && chmod +x /entry.sh
|
||||||
|
|
||||||
|
VOLUME /acme.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entry.sh"]
|
||||||
|
CMD ["--help"]
|
||||||
674
LICENSE.md
Normal file
674
LICENSE.md
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
490
README.md
490
README.md
@@ -1,98 +1,152 @@
|
|||||||
# An ACME Shell script: acme.sh
|
# An ACME Shell script: acme.sh
|
||||||
|
|
||||||
|
[](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)
|
||||||
|
[](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)
|
||||||
|
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)
|
||||||
|
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
|
||||||
|
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
[](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
||||||
|
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- An ACME protocol client written purely in Shell (Unix shell) language.
|
- An ACME protocol client written purely in Shell (Unix shell) language.
|
||||||
- Fully ACME protocol implementation.
|
- Full ACME protocol implementation.
|
||||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn.
|
- 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.
|
- Bash, dash and sh compatible.
|
||||||
- Simplest shell script for Let's Encrypt free certificate client.
|
- Purely written in Shell with no dependencies on python.
|
||||||
- Purely written in Shell with no dependencies on python or Let's Encrypt official client.
|
- Just one script to issue, renew and install your certificates automatically.
|
||||||
- Just one script, to issue, renew and install your certificates automatically.
|
|
||||||
- DOES NOT require `root/sudoer` access.
|
- DOES NOT require `root/sudoer` access.
|
||||||
|
- Docker ready
|
||||||
|
- IPv6 ready
|
||||||
|
- Cron job notifications for renewal or error etc.
|
||||||
|
|
||||||
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
|
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates.
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
||||||
|
|
||||||
|
|
||||||
Wiki: https://github.com/Neilpang/acme.sh/wiki
|
# [中文说明](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/)
|
||||||
|
- [ruby-china.org](https://ruby-china.org/topics/31983)
|
||||||
|
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||||
|
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
|
||||||
|
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
|
||||||
|
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
|
||||||
|
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
|
||||||
|
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
|
||||||
|
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
|
||||||
|
- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
|
||||||
|
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
|
||||||
|
- [CentOS Web Panel](http://centos-webpanel.com/)
|
||||||
|
- [lnmp.org](https://lnmp.org/)
|
||||||
|
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
|
||||||
|
|
||||||
# Tested OS
|
# Tested OS
|
||||||
|
|
||||||
| NO | Status| Platform|
|
| NO | Status| Platform|
|
||||||
|----|-------|---------|
|
|----|-------|---------|
|
||||||
|1|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu
|
|1|[](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)|Mac OSX
|
||||||
|2|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian
|
|2|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)|Windows (cygwin with curl, openssl and crontab included)
|
||||||
|3|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS
|
|3|[](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)|FreeBSD
|
||||||
|4|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|
|4|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)|Solaris
|
||||||
|5|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD
|
|5|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)| Ubuntu
|
||||||
|6|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense
|
|6|NA|pfsense
|
||||||
|7|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE
|
|7|NA|OpenBSD
|
||||||
|8|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl)
|
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
|
||||||
|9|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux
|
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
|
||||||
|10|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora
|
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
|
||||||
|11|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux
|
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
|
||||||
|12|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux
|
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
|
||||||
|13|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
|
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
|
||||||
|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111
|
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
|
||||||
|15|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD
|
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
|
||||||
|16|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia
|
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
|
||||||
|
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
|
||||||
|
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
|
||||||
|
|19|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|
||||||
|
|20|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
||||||
|
|21|[](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)
|
||||||
|
|
||||||
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest):
|
|
||||||
|
|
||||||
https://github.com/Neilpang/acmetest
|
Check our [testing project](https://github.com/acmesh-official/acmetest):
|
||||||
|
|
||||||
# Supported Mode
|
https://github.com/acmesh-official/acmetest
|
||||||
|
|
||||||
1. Webroot mode
|
# Supported CA
|
||||||
2. Standalone mode
|
|
||||||
3. Apache mode
|
|
||||||
4. Dns mode
|
|
||||||
|
|
||||||
# Upgrade from 1.x to 2.x
|
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
|
||||||
|
- Letsencrypt.org CA
|
||||||
|
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
|
||||||
|
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
|
||||||
|
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
|
||||||
|
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
|
||||||
|
|
||||||
You can simply uninstall 1.x and re-install 2.x.
|
# Supported modes
|
||||||
2.x is 100% compatible to 1.x. You will feel right at home as if nothing has changed.
|
|
||||||
|
|
||||||
# le.sh renamed to acme.sh NOW!
|
- Webroot mode
|
||||||
|
- 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)
|
||||||
|
|
||||||
All configurations are 100% compatible between `le.sh` and `acme.sh`. You just need to uninstall `le.sh` and re-install `acme.sh` again.
|
|
||||||
Nothing will be broken during the process.
|
|
||||||
|
|
||||||
# How to install
|
# 1. How to install
|
||||||
|
|
||||||
### 1. Install online:
|
### 1. Install online
|
||||||
|
|
||||||
Check this project: https://github.com/Neilpang/get.acme.sh
|
Check this project: https://github.com/acmesh-official/get.acme.sh
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://get.acme.sh | sh
|
curl https://get.acme.sh | sh -s email=my@example.com
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or:
|
Or:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget -O - https://get.acme.sh | sh
|
wget -O - https://get.acme.sh | sh -s email=my@example.com
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### 2. Or, Install from git:
|
### 2. Or, Install from git
|
||||||
|
|
||||||
Clone this project:
|
Clone this project and launch installation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Neilpang/acme.sh.git
|
git clone https://github.com/acmesh-official/acme.sh.git
|
||||||
cd ./acme.sh
|
cd ./acme.sh
|
||||||
./acme.sh --install
|
./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/Neilpang/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.
|
All certs will be placed in this folder too.
|
||||||
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`.
|
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`.
|
||||||
3. Create everyday cron job to check and renew the cert if needed.
|
3. Create daily cron job to check and renew the certs if needed.
|
||||||
|
|
||||||
Cron entry example:
|
Cron entry example:
|
||||||
|
|
||||||
@@ -100,181 +154,227 @@ 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 current terminal and reopen again 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.
|
||||||
|
|
||||||
Ok, you are ready to issue cert now.
|
|
||||||
Show help message:
|
Show help message:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
|
|
||||||
root@v1:~# acme.sh -h
|
root@v1:~# acme.sh -h
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Just issue a cert:
|
# 2. Just issue a cert
|
||||||
|
|
||||||
**Example 1:** Single domain.
|
**Example 1:** Single domain.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -d aa.com -w /home/wwwroot/aa.com
|
acme.sh --issue -d example.com -w /home/wwwroot/example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
acme.sh --issue -d example.com -w /home/username/public_html
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
acme.sh --issue -d example.com -w /var/www/html
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example 2:** Multiple domains in the same cert.
|
**Example 2:** Multiple domains in the same cert.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -d aa.com -d www.aa.com -d cp.aa.com -w /home/wwwroot/aa.com
|
acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
The parameter `/home/wwwroot/aa.com` is the web root folder. You **MUST** have `write access` to this folder.
|
The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder.
|
||||||
|
|
||||||
Second argument **"aa.com"** is the main domain you want to issue cert for.
|
Second argument **"example.com"** is the main domain you want to issue the cert for.
|
||||||
You must have at least a domain there.
|
You must have at least one domain there.
|
||||||
|
|
||||||
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/aa.com`.
|
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`.
|
||||||
|
|
||||||
Generate/issued certs will be placed in `~/.acme.sh/aa.com/`
|
The certs will be placed in `~/.acme.sh/example.com/`
|
||||||
|
|
||||||
The issued cert will be renewed every 80 days automatically.
|
The certs will be renewed automatically every **60** days.
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
|
|
||||||
# Install issued cert to apache/nginx etc.
|
# 3. Install the cert to Apache/Nginx etc.
|
||||||
|
|
||||||
After you issue a cert, you probably want to install the cert with your nginx/apache or other servers you may be using.
|
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:
|
||||||
```bash
|
```bash
|
||||||
acme.sh --installcert -d aa.com \
|
acme.sh --install-cert -d example.com \
|
||||||
--certpath /path/to/certfile/in/apache/nginx \
|
--cert-file /path/to/certfile/in/apache/cert.pem \
|
||||||
--keypath /path/to/keyfile/in/apache/nginx \
|
--key-file /path/to/keyfile/in/apache/key.pem \
|
||||||
--capath /path/to/ca/certfile/apache/nginx \
|
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
|
||||||
--fullchainpath path/to/fullchain/certfile/apache/nginx \
|
--reloadcmd "service apache2 force-reload"
|
||||||
--reloadcmd "service apache2|nginx reload"
|
```
|
||||||
|
|
||||||
|
**Nginx** example:
|
||||||
|
```bash
|
||||||
|
acme.sh --install-cert -d example.com \
|
||||||
|
--key-file /path/to/keyfile/in/nginx/key.pem \
|
||||||
|
--fullchain-file /path/to/fullchain/nginx/cert.pem \
|
||||||
|
--reloadcmd "service nginx force-reload"
|
||||||
```
|
```
|
||||||
|
|
||||||
Only the domain is required, all the other parameters are optional.
|
Only the domain is required, all the other parameters are optional.
|
||||||
|
|
||||||
Install the issued cert/key to the production apache or nginx path.
|
The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission.
|
||||||
|
|
||||||
The cert will be `renewed every 80 days by default` (which is configurable). Once the cert is renewed, the apache/nginx will be automatically reloaded by the command: `service apache2 reload` or `service nginx reload`.
|
Install/copy the cert/key to the production Apache or Nginx path.
|
||||||
|
|
||||||
# Use Standalone server to issue cert
|
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`.
|
||||||
|
|
||||||
**(requires you be root/sudoer, or you have permission to listen tcp 80 port)**
|
|
||||||
|
|
||||||
The tcp `80` port **MUST** be free to listen, otherwise you will be prompted to free the `80` port and try again.
|
**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
|
||||||
|
|
||||||
|
**(requires you to be root/sudoer or have 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.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --standalone -d aa.com -d www.aa.com -d cp.aa.com
|
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# Use Standalone tls server to issue cert
|
# 5. Use Standalone ssl server to issue cert
|
||||||
|
|
||||||
**(requires you be root/sudoer, or you have permission to listen tcp 443 port)**
|
**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
|
||||||
|
|
||||||
acme.sh supports `tls-sni-01` validation.
|
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
||||||
|
|
||||||
The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --tls -d aa.com -d www.aa.com -d cp.aa.com
|
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# Use Apache mode
|
|
||||||
|
|
||||||
**(requires you be root/sudoer, since it is required to interact with apache server)**
|
# 6. Use Apache mode
|
||||||
|
|
||||||
If you are running a web server, apache or nginx, it is recommended to use the `Webroot mode`.
|
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
|
||||||
|
|
||||||
Particularly, if you are running an apache server, you should use apache mode instead. This mode doesn't write any files to your web root folder.
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
Just set string "apache" as the second argument, it will force use of apache plugin automatically.
|
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.
|
||||||
acme.sh --issue --apache -d aa.com -d www.aa.com -d user.aa.com
|
|
||||||
|
```sh
|
||||||
|
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
**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.**
|
||||||
|
|
||||||
# Use DNS mode:
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
Support the `dns-01` challenge.
|
# 7. Use Nginx mode
|
||||||
|
|
||||||
|
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
|
||||||
|
|
||||||
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
|
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
|
||||||
|
|
||||||
|
Just set string "nginx" as the second argument.
|
||||||
|
|
||||||
|
It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
|
||||||
|
|
||||||
|
So, the config is not changed.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
|
||||||
|
# 8. Automatic DNS API integration
|
||||||
|
|
||||||
|
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!
|
||||||
|
|
||||||
|
### Currently acme.sh supports most of the dns providers:
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||||
|
|
||||||
|
# 9. Use DNS manual mode:
|
||||||
|
|
||||||
|
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
|
||||||
|
|
||||||
|
If your dns provider doesn't support any api access, you can add the txt record by hand.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --dns -d aa.com -d www.aa.com -d user.aa.com
|
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
You should get the output like below:
|
You should get an output like below:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
Add the following txt record:
|
Add the following txt record:
|
||||||
Domain:_acme-challenge.aa.com
|
Domain:_acme-challenge.example.com
|
||||||
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
|
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
|
||||||
|
|
||||||
Add the following txt record:
|
Add the following txt record:
|
||||||
Domain:_acme-challenge.www.aa.com
|
Domain:_acme-challenge.www.example.com
|
||||||
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
Please add those txt records to the domains. Waiting for the dns to take effect.
|
Please add those txt records to the domains. Waiting for the dns to take effect.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then just rerun with `renew` argument:
|
Then just rerun with `renew` argument:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --renew -d aa.com
|
acme.sh --renew -d example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Ok, it's finished.
|
Ok, it's done.
|
||||||
|
|
||||||
# Automatic DNS API integration
|
**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.**
|
||||||
|
|
||||||
If your DNS provider supports API access, we can use API to automatically issue the certs.
|
**Please use dns api mode instead.**
|
||||||
|
|
||||||
You don't have do anything manually!
|
# 10. Issue ECC certificates
|
||||||
|
|
||||||
### Currently acme.sh supports:
|
`Let's Encrypt` can now issue **ECDSA** certificates.
|
||||||
|
|
||||||
1. Cloudflare.com API
|
And we support them too!
|
||||||
2. Dnspod.cn API
|
|
||||||
3. Cloudxns.com API
|
|
||||||
4. AWS Route 53, see: https://github.com/Neilpang/acme.sh/issues/65
|
|
||||||
5. lexicon dns api: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
|
|
||||||
(DigitalOcean, DNSimple, DnsMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
|
|
||||||
|
|
||||||
##### More APIs are coming soon...
|
Just set the `keylength` parameter with a prefix `ec-`.
|
||||||
|
|
||||||
If your DNS provider is not on the supported list above, you can write your own script API easily. If you do please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute to the project.
|
|
||||||
|
|
||||||
For more details: [How to use dns api](dnsapi)
|
|
||||||
|
|
||||||
# Issue ECC certificate:
|
|
||||||
|
|
||||||
`Let's Encrypt` now can issue **ECDSA** certificates.
|
|
||||||
|
|
||||||
And we also support it.
|
|
||||||
|
|
||||||
Just set the `length` parameter with a prefix `ec-`.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
### Single domain ECC cerfiticate:
|
### Single domain ECC certificate
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -w /home/wwwroot/aa.com -d aa.com --keylength ec-256
|
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
|
||||||
```
|
```
|
||||||
|
|
||||||
SAN multi domain ECC certificate:
|
### SAN multi domain ECC certificate
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue -w /home/wwwroot/aa.com -d aa.com -d www.aa.com --keylength ec-256
|
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
|
||||||
```
|
```
|
||||||
|
|
||||||
Please look at the last parameter above.
|
Please look at the `keylength` parameter above.
|
||||||
|
|
||||||
Valid values are:
|
Valid values are:
|
||||||
|
|
||||||
@@ -282,24 +382,138 @@ Valid values are:
|
|||||||
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
||||||
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
||||||
|
|
||||||
# Under the Hood
|
|
||||||
|
|
||||||
|
# 11. Issue Wildcard certificates
|
||||||
|
|
||||||
|
It's simple, just give a wildcard domain as the `-d` parameter.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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.
|
||||||
|
|
||||||
|
However, you can also force to renew a cert:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --renew -d example.com --force
|
||||||
|
```
|
||||||
|
|
||||||
|
or, for ECC cert:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --renew -d example.com --force --ecc
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# 13. How to stop cert renewal
|
||||||
|
|
||||||
|
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --remove -d example.com [--ecc]
|
||||||
|
```
|
||||||
|
|
||||||
|
The cert/key file is not removed from the disk.
|
||||||
|
|
||||||
|
You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
|
||||||
|
|
||||||
|
|
||||||
|
# 14. How to upgrade `acme.sh`
|
||||||
|
|
||||||
|
acme.sh is in constant development, so it's strongly recommended to use the latest code.
|
||||||
|
|
||||||
|
You can update acme.sh to the latest code:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also enable auto upgrade:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --upgrade --auto-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
Then **acme.sh** will be kept up to date automatically.
|
||||||
|
|
||||||
|
Disable auto upgrade:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --upgrade --auto-upgrade 0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# 15. Issue a cert from an existing CSR
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
||||||
|
|
||||||
|
|
||||||
|
# 16. Send notifications in cronjob
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acme.sh/wiki/notify
|
||||||
|
|
||||||
|
|
||||||
|
# 17. Under the Hood
|
||||||
|
|
||||||
Speak ACME language using shell, directly to "Let's Encrypt".
|
Speak ACME language using shell, directly to "Let's Encrypt".
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
# Acknowledgment
|
|
||||||
|
# 18. Acknowledgments
|
||||||
|
|
||||||
1. Acme-tiny: https://github.com/diafygi/acme-tiny
|
1. Acme-tiny: https://github.com/diafygi/acme-tiny
|
||||||
2. ACME protocol: https://github.com/ietf-wg-acme/acme
|
2. ACME protocol: https://github.com/ietf-wg-acme/acme
|
||||||
3. Certbot: https://github.com/certbot/certbot
|
|
||||||
|
|
||||||
# License & Other
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
### Code Contributors
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
### Financial Contributors
|
||||||
|
|
||||||
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
||||||
|
|
||||||
|
#### Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
|
#### 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)]
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/0/website"><img src="https://opencollective.com/acmesh/organization/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/1/website"><img src="https://opencollective.com/acmesh/organization/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/2/website"><img src="https://opencollective.com/acmesh/organization/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/3/website"><img src="https://opencollective.com/acmesh/organization/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/4/website"><img src="https://opencollective.com/acmesh/organization/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/5/website"><img src="https://opencollective.com/acmesh/organization/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/6/website"><img src="https://opencollective.com/acmesh/organization/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/acmesh/organization/7/website"><img src="https://opencollective.com/acmesh/organization/7/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>
|
||||||
|
|
||||||
|
# 19. License & Others
|
||||||
|
|
||||||
License is GPLv3
|
License is GPLv3
|
||||||
|
|
||||||
Please Star and Fork me.
|
Please Star and Fork me.
|
||||||
|
|
||||||
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcomed.
|
[Issues](https://github.com/acmesh-official/acme.sh/issues) and [pull requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
|
||||||
|
|
||||||
|
|
||||||
|
# 20. Donate
|
||||||
|
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)
|
||||||
|
|||||||
6
deploy/README.md
Normal file
6
deploy/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Using deploy api
|
||||||
|
|
||||||
|
deploy hook usage:
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acme.sh/wiki/deployhooks
|
||||||
|
|
||||||
26
deploy/apache.sh
Normal file
26
deploy/apache.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to apache server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
apache_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "Deploy cert to apache server, Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
92
deploy/cleverreach.sh
Normal file
92
deploy/cleverreach.sh
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Here is the script to deploy the cert to your CleverReach Account using the CleverReach REST API.
|
||||||
|
# Your OAuth needs the right scope, please contact CleverReach support for that.
|
||||||
|
#
|
||||||
|
# Written by Jan-Philipp Benecke <github@bnck.me>
|
||||||
|
# Public domain, 2020
|
||||||
|
#
|
||||||
|
# Following environment variables must be set:
|
||||||
|
#
|
||||||
|
#export DEPLOY_CLEVERREACH_CLIENT_ID=myid
|
||||||
|
#export DEPLOY_CLEVERREACH_CLIENT_SECRET=mysecret
|
||||||
|
|
||||||
|
cleverreach_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_rest_endpoint="https://rest.cleverreach.com"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID
|
||||||
|
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then
|
||||||
|
_err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" ]; then
|
||||||
|
_err "CleverReach client secret is not found, please define DEPLOY_CLEVERREACH_CLIENT_SECRET."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}"
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}"
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
|
||||||
|
_info "Obtaining a CleverReach access token"
|
||||||
|
|
||||||
|
_data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}"
|
||||||
|
_auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
_debug _data "$_data"
|
||||||
|
_debug _auth_result "$_auth_result"
|
||||||
|
|
||||||
|
_regex=".*\"access_token\":\"\([-._0-9A-Za-z]*\)\".*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p")
|
||||||
|
|
||||||
|
_debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
|
||||||
|
if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
|
||||||
|
_info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
export _H1="Authorization: Bearer ${_access_token}"
|
||||||
|
_subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")"
|
||||||
|
_access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p")
|
||||||
|
|
||||||
|
_debug _subclient_token_result "$_access_token"
|
||||||
|
|
||||||
|
_info "Destroying parent token at CleverReach, as it not needed anymore"
|
||||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
|
||||||
|
_debug _destroy_result "$_destroy_result"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Uploading certificate and key to CleverReach"
|
||||||
|
|
||||||
|
_certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}"
|
||||||
|
export _H1="Authorization: Bearer ${_access_token}"
|
||||||
|
_add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
|
||||||
|
_info "Destroying token at CleverReach, as it not needed anymore"
|
||||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
|
||||||
|
_debug _destroy_result "$_destroy_result"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then
|
||||||
|
_info "Uploaded certificate successfully"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug _add_cert_result "$_add_cert_result"
|
||||||
|
_err "Unable to update certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
98
deploy/consul.sh
Normal file
98
deploy/consul.sh
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to hashicorp consul using curl
|
||||||
|
# (https://www.consul.io/)
|
||||||
|
#
|
||||||
|
# it requires following environment variables:
|
||||||
|
#
|
||||||
|
# CONSUL_PREFIX - this contains the prefix path in consul
|
||||||
|
# CONSUL_HTTP_ADDR - consul requires this to find your consul server
|
||||||
|
#
|
||||||
|
# additionally, you need to ensure that CONSUL_HTTP_TOKEN is available
|
||||||
|
# to access the consul server
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
consul_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# validate required env vars
|
||||||
|
_getdeployconf CONSUL_PREFIX
|
||||||
|
if [ -z "$CONSUL_PREFIX" ]; then
|
||||||
|
_err "CONSUL_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf CONSUL_PREFIX "$CONSUL_PREFIX"
|
||||||
|
|
||||||
|
_getdeployconf CONSUL_HTTP_ADDR
|
||||||
|
if [ -z "$CONSUL_HTTP_ADDR" ]; then
|
||||||
|
_err "CONSUL_HTTP_ADDR needs to be defined (contains consul connection address)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf CONSUL_HTTP_ADDR "$CONSUL_HTTP_ADDR"
|
||||||
|
|
||||||
|
CONSUL_CMD=$(command -v consul)
|
||||||
|
|
||||||
|
# force CLI, but the binary does not exist => error
|
||||||
|
if [ -n "$USE_CLI" ] && [ -z "$CONSUL_CMD" ]; then
|
||||||
|
_err "Cannot find the consul binary!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use the CLI first
|
||||||
|
if [ -n "$USE_CLI" ] || [ -n "$CONSUL_CMD" ]; then
|
||||||
|
_info "Found consul binary, deploying with CLI"
|
||||||
|
consul_deploy_cli "$CONSUL_CMD" "$CONSUL_PREFIX"
|
||||||
|
else
|
||||||
|
_info "Did not find consul binary, deploying with API"
|
||||||
|
consul_deploy_api "$CONSUL_HTTP_ADDR" "$CONSUL_PREFIX" "$CONSUL_HTTP_TOKEN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
consul_deploy_api() {
|
||||||
|
CONSUL_HTTP_ADDR="$1"
|
||||||
|
CONSUL_PREFIX="$2"
|
||||||
|
CONSUL_HTTP_TOKEN="$3"
|
||||||
|
|
||||||
|
URL="$CONSUL_HTTP_ADDR/v1/kv/$CONSUL_PREFIX"
|
||||||
|
export _H1="X-Consul-Token: $CONSUL_HTTP_TOKEN"
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}-cert.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}-key.pem" '' "PUT" || return 1
|
||||||
|
else
|
||||||
|
_post "$(cat "$_ccert")" "$URL/${_cdomain}/cert.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}/cert.key" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_cca")" "$URL/${_cdomain}/chain.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}/fullchain.pem" '' "PUT" || return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
consul_deploy_cli() {
|
||||||
|
CONSUL_CMD="$1"
|
||||||
|
CONSUL_PREFIX="$2"
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-cert.pem" @"$_cfullchain" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-key.pem" @"$_ckey" || return 1
|
||||||
|
else
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
62
deploy/cpanel_uapi.sh
Normal file
62
deploy/cpanel_uapi.sh
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Here is the script to deploy the cert to your cpanel using the cpanel API.
|
||||||
|
# Uses command line uapi. --user option is needed only if run as root.
|
||||||
|
# Returns 0 when success.
|
||||||
|
#
|
||||||
|
# Please note that I am no longer using Github. If you want to report an issue
|
||||||
|
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
|
||||||
|
#
|
||||||
|
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
|
||||||
|
# Public domain, 2017-2018
|
||||||
|
|
||||||
|
#export DEPLOY_CPANEL_USER=myusername
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
|
||||||
|
cpanel_uapi_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if ! _exists uapi; then
|
||||||
|
_err "The command uapi is not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode <"$_ccert")
|
||||||
|
_key=$(_url_encode <"$_ckey")
|
||||||
|
|
||||||
|
_debug _cert "$_cert"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
if [ "$(id -u)" = 0 ]; then
|
||||||
|
if [ -z "$DEPLOY_CPANEL_USER" ]; then
|
||||||
|
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
|
||||||
|
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
||||||
|
else
|
||||||
|
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
||||||
|
fi
|
||||||
|
error_response="status: 0"
|
||||||
|
if test "${_response#*$error_response}" != "$_response"; then
|
||||||
|
_err "Error in deploying certificate:"
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug response "$_response"
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
288
deploy/docker.sh
Executable file
288
deploy/docker.sh
Executable file
@@ -0,0 +1,288 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_LABEL="xxxxxxx"
|
||||||
|
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_KEY_FILE="/path/to/key.pem"
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_CERT_FILE="/path/to/cert.pem"
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_CA_FILE="/path/to/ca.pem"
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem"
|
||||||
|
#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
|
||||||
|
|
||||||
|
_DEPLOY_DOCKER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/deploy-to-docker-containers"
|
||||||
|
|
||||||
|
_DOCKER_HOST_DEFAULT="/var/run/docker.sock"
|
||||||
|
|
||||||
|
docker_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL"
|
||||||
|
if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then
|
||||||
|
_err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container."
|
||||||
|
_err "See: $_DEPLOY_DOCKER_WIKI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL"
|
||||||
|
|
||||||
|
if [ "$DOCKER_HOST" ]; then
|
||||||
|
_saveaccountconf DOCKER_HOST "$DOCKER_HOST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _exists docker && docker version | grep -i docker >/dev/null; then
|
||||||
|
_info "Using docker command"
|
||||||
|
export _USE_DOCKER_COMMAND=1
|
||||||
|
else
|
||||||
|
export _USE_DOCKER_COMMAND=
|
||||||
|
fi
|
||||||
|
|
||||||
|
export _USE_UNIX_SOCKET=
|
||||||
|
if [ -z "$_USE_DOCKER_COMMAND" ]; then
|
||||||
|
export _USE_REST=
|
||||||
|
if [ "$DOCKER_HOST" ]; then
|
||||||
|
_debug "Try use docker host: $DOCKER_HOST"
|
||||||
|
export _USE_REST=1
|
||||||
|
else
|
||||||
|
export _DOCKER_SOCK="$_DOCKER_HOST_DEFAULT"
|
||||||
|
_debug "Try use $_DOCKER_SOCK"
|
||||||
|
if [ ! -e "$_DOCKER_SOCK" ] || [ ! -w "$_DOCKER_SOCK" ]; then
|
||||||
|
_err "$_DOCKER_SOCK is not available"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
export _USE_UNIX_SOCKET=1
|
||||||
|
if ! _exists "curl"; then
|
||||||
|
_err "Please install curl first."
|
||||||
|
_err "We need curl to work."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _check_curl_version; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE"
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
|
||||||
|
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
||||||
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" "base64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")"
|
||||||
|
_info "Container id: $_cid"
|
||||||
|
if [ -z "$_cid" ]; then
|
||||||
|
_err "can not find container id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then
|
||||||
|
if ! _docker_cp "$_cid" "$_ckey" "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then
|
||||||
|
if ! _docker_cp "$_cid" "$_ccert" "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then
|
||||||
|
if ! _docker_cp "$_cid" "$_cca" "$DEPLOY_DOCKER_CONTAINER_CA_FILE"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then
|
||||||
|
if ! _docker_cp "$_cid" "$_cfullchain" "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
||||||
|
_info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
||||||
|
if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#label
|
||||||
|
_get_id() {
|
||||||
|
_label="$1"
|
||||||
|
if [ "$_USE_DOCKER_COMMAND" ]; then
|
||||||
|
docker ps -f label="$_label" --format "{{.ID}}"
|
||||||
|
elif [ "$_USE_REST" ]; then
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
elif [ "$_USE_UNIX_SOCKET" ]; then
|
||||||
|
_req="{\"label\":[\"$_label\"]}"
|
||||||
|
_debug2 _req "$_req"
|
||||||
|
_req="$(printf "%s" "$_req" | _url_encode)"
|
||||||
|
_debug2 _req "$_req"
|
||||||
|
listjson="$(_curl_unix_sock "${_DOCKER_SOCK:-$_DOCKER_HOST_DEFAULT}" GET "/containers/json?filters=$_req")"
|
||||||
|
_debug2 "listjson" "$listjson"
|
||||||
|
echo "$listjson" | tr '{,' '\n' | grep -i '"id":' | _head_n 1 | cut -d '"' -f 4
|
||||||
|
else
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#id cmd
|
||||||
|
_docker_exec() {
|
||||||
|
_eargs="$*"
|
||||||
|
_debug2 "_docker_exec $_eargs"
|
||||||
|
_dcid="$1"
|
||||||
|
shift
|
||||||
|
if [ "$_USE_DOCKER_COMMAND" ]; then
|
||||||
|
docker exec -i "$_dcid" sh -c "$*"
|
||||||
|
elif [ "$_USE_REST" ]; then
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
elif [ "$_USE_UNIX_SOCKET" ]; then
|
||||||
|
_cmd="$*"
|
||||||
|
#_cmd="$(printf "%s" "$_cmd" | sed 's/ /","/g')"
|
||||||
|
_debug2 _cmd "$_cmd"
|
||||||
|
#create exec instance:
|
||||||
|
cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"sh\", \"-c\", \"$_cmd\"]}")"
|
||||||
|
_debug2 cjson "$cjson"
|
||||||
|
execid="$(echo "$cjson" | cut -d '"' -f 4)"
|
||||||
|
_debug execid "$execid"
|
||||||
|
ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")"
|
||||||
|
_debug2 ejson "$ejson"
|
||||||
|
if [ "$ejson" ]; then
|
||||||
|
_err "$ejson"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#id from to
|
||||||
|
_docker_cp() {
|
||||||
|
_dcid="$1"
|
||||||
|
_from="$2"
|
||||||
|
_to="$3"
|
||||||
|
_info "Copying file from $_from to $_to"
|
||||||
|
_dir="$(dirname "$_to")"
|
||||||
|
_debug2 _dir "$_dir"
|
||||||
|
if ! _docker_exec "$_dcid" mkdir -p "$_dir"; then
|
||||||
|
_err "Can not create dir: $_dir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ "$_USE_DOCKER_COMMAND" ]; then
|
||||||
|
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
|
||||||
|
_docker_exec "$_dcid" tee "$_to" <"$_from"
|
||||||
|
else
|
||||||
|
_docker_exec "$_dcid" tee "$_to" <"$_from" >/dev/null
|
||||||
|
fi
|
||||||
|
if [ "$?" = "0" ]; then
|
||||||
|
_info "Success"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_info "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
elif [ "$_USE_REST" ]; then
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
elif [ "$_USE_UNIX_SOCKET" ]; then
|
||||||
|
_frompath="$_from"
|
||||||
|
if _startswith "$_frompath" '/'; then
|
||||||
|
_frompath="$(echo "$_from" | cut -b 2-)" #remove the first '/' char
|
||||||
|
fi
|
||||||
|
_debug2 "_frompath" "$_frompath"
|
||||||
|
_toname="$(basename "$_to")"
|
||||||
|
_debug2 "_toname" "$_toname"
|
||||||
|
_debug2 "_from" "$_from"
|
||||||
|
if ! tar --transform="s,$(printf "%s" "$_frompath" | tr '*' .),$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then
|
||||||
|
_err "copy error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Not implemented yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#sock method endpoint data content-type
|
||||||
|
_curl_unix_sock() {
|
||||||
|
_socket="$1"
|
||||||
|
_method="$2"
|
||||||
|
_endpoint="$3"
|
||||||
|
_data="$4"
|
||||||
|
_ctype="$5"
|
||||||
|
if [ -z "$_ctype" ]; then
|
||||||
|
_ctype="Content-Type: application/json"
|
||||||
|
fi
|
||||||
|
_debug _data "$_data"
|
||||||
|
_debug2 "url" "http://localhost$_endpoint"
|
||||||
|
if [ "$_CURL_NO_HOST" ]; then
|
||||||
|
_cux_url="http:$_endpoint"
|
||||||
|
else
|
||||||
|
_cux_url="http://localhost$_endpoint"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
|
||||||
|
curl -vvv --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url"
|
||||||
|
else
|
||||||
|
curl --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_curl_version() {
|
||||||
|
_cversion="$(curl -V | grep '^curl ' | cut -d ' ' -f 2)"
|
||||||
|
_debug2 "_cversion" "$_cversion"
|
||||||
|
|
||||||
|
_major="$(_getfield "$_cversion" 1 '.')"
|
||||||
|
_debug2 "_major" "$_major"
|
||||||
|
|
||||||
|
_minor="$(_getfield "$_cversion" 2 '.')"
|
||||||
|
_debug2 "_minor" "$_minor"
|
||||||
|
|
||||||
|
if [ "$_major$_minor" -lt "740" ]; then
|
||||||
|
_err "curl v$_cversion doesn't support unit socket"
|
||||||
|
_err "Please upgrade to curl 7.40 or later."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ "$_major$_minor" -lt "750" ]; then
|
||||||
|
_debug "Use short host name"
|
||||||
|
export _CURL_NO_HOST=1
|
||||||
|
else
|
||||||
|
export _CURL_NO_HOST=
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
26
deploy/dovecot.sh
Normal file
26
deploy/dovecot.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to dovecot server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
dovecot_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
114
deploy/exim4.sh
Normal file
114
deploy/exim4.sh
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to exim4 server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
|
||||||
|
#DEPLOY_EXIM4_RELOAD="service exim4 restart"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
exim4_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_ssl_path="/etc/acme.sh/exim4"
|
||||||
|
if ! mkdir -p "$_ssl_path"; then
|
||||||
|
_err "Can not create folder:$_ssl_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Copying key and cert"
|
||||||
|
_real_key="$_ssl_path/exim4.key"
|
||||||
|
if ! cat "$_ckey" >"$_real_key"; then
|
||||||
|
_err "Error: write key file to: $_real_key"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_real_fullchain="$_ssl_path/exim4.pem"
|
||||||
|
if ! cat "$_cfullchain" >"$_real_fullchain"; then
|
||||||
|
_err "Error: write key file to: $_real_fullchain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEFAULT_EXIM4_RELOAD="service exim4 restart"
|
||||||
|
_reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}"
|
||||||
|
|
||||||
|
if [ -z "$IS_RENEW" ]; then
|
||||||
|
DEFAULT_EXIM4_CONF="/etc/exim/exim.conf"
|
||||||
|
if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then
|
||||||
|
DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template"
|
||||||
|
fi
|
||||||
|
_exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}"
|
||||||
|
_debug _exim4_conf "$_exim4_conf"
|
||||||
|
if [ ! -f "$_exim4_conf" ]; then
|
||||||
|
if [ -z "$DEPLOY_EXIM4_CONF" ]; then
|
||||||
|
_err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_err "It seems that the specified exim4 conf is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ ! -w "$_exim4_conf" ]; then
|
||||||
|
_err "The file $_exim4_conf is not writable, please change the permission."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak"
|
||||||
|
_info "Backup $_exim4_conf to $_backup_conf"
|
||||||
|
cp "$_exim4_conf" "$_backup_conf"
|
||||||
|
|
||||||
|
_info "Modify exim4 conf: $_exim4_conf"
|
||||||
|
if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" &&
|
||||||
|
_setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
|
||||||
|
_info "Set config success!"
|
||||||
|
else
|
||||||
|
_err "Config exim4 server error, please report bug to us."
|
||||||
|
_info "Restoring exim4 conf"
|
||||||
|
if cat "$_backup_conf" >"$_exim4_conf"; then
|
||||||
|
_info "Restore conf success"
|
||||||
|
eval "$_reload"
|
||||||
|
else
|
||||||
|
_err "Oops, error restore exim4 conf, please report bug to us."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Run reload: $_reload"
|
||||||
|
if eval "$_reload"; then
|
||||||
|
_info "Reload success!"
|
||||||
|
if [ "$DEPLOY_EXIM4_CONF" ]; then
|
||||||
|
_savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF"
|
||||||
|
else
|
||||||
|
_cleardomainconf DEPLOY_EXIM4_CONF
|
||||||
|
fi
|
||||||
|
if [ "$DEPLOY_EXIM4_RELOAD" ]; then
|
||||||
|
_savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD"
|
||||||
|
else
|
||||||
|
_cleardomainconf DEPLOY_EXIM4_RELOAD
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Reload error, restoring"
|
||||||
|
if cat "$_backup_conf" >"$_exim4_conf"; then
|
||||||
|
_info "Restore conf success"
|
||||||
|
eval "$_reload"
|
||||||
|
else
|
||||||
|
_err "Oops, error restore exim4 conf, please report bug to us."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
126
deploy/fritzbox.sh
Normal file
126
deploy/fritzbox.sh
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to an AVM FRITZ!Box router.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#DEPLOY_FRITZBOX_USERNAME="username"
|
||||||
|
#DEPLOY_FRITZBOX_PASSWORD="password"
|
||||||
|
#DEPLOY_FRITZBOX_URL="https://fritz.box"
|
||||||
|
|
||||||
|
# Kudos to wikrie at Github for his FRITZ!Box update script:
|
||||||
|
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
fritzbox_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if ! _exists iconv; then
|
||||||
|
if ! _exists uconv; then
|
||||||
|
if ! _exists perl; then
|
||||||
|
_err "iconv or uconv or perl not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear traces of incorrectly stored values
|
||||||
|
_clearaccountconf DEPLOY_FRITZBOX_USERNAME
|
||||||
|
_clearaccountconf DEPLOY_FRITZBOX_PASSWORD
|
||||||
|
_clearaccountconf DEPLOY_FRITZBOX_URL
|
||||||
|
|
||||||
|
# Read config from saved values or env
|
||||||
|
_getdeployconf DEPLOY_FRITZBOX_USERNAME
|
||||||
|
_getdeployconf DEPLOY_FRITZBOX_PASSWORD
|
||||||
|
_getdeployconf DEPLOY_FRITZBOX_URL
|
||||||
|
|
||||||
|
_debug DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
|
||||||
|
_debug DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
|
||||||
|
_secure_debug DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_FRITZBOX_USERNAME" ]; then
|
||||||
|
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$DEPLOY_FRITZBOX_PASSWORD" ]; then
|
||||||
|
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$DEPLOY_FRITZBOX_URL" ]; then
|
||||||
|
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save current values
|
||||||
|
_savedeployconf DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
|
||||||
|
_savedeployconf DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
|
||||||
|
_savedeployconf DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
|
||||||
|
|
||||||
|
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
|
||||||
|
_info "Log in to the FRITZ!Box"
|
||||||
|
_fritzbox_challenge="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
|
||||||
|
if _exists iconv; then
|
||||||
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | iconv -f ASCII -t UTF16LE | _digest md5 hex)"
|
||||||
|
elif _exists uconv; then
|
||||||
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | uconv -f ASCII -t UTF16LE | _digest md5 hex)"
|
||||||
|
else
|
||||||
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | _digest md5 hex)"
|
||||||
|
fi
|
||||||
|
_fritzbox_sid="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua?sid=0000000000000000&username=${DEPLOY_FRITZBOX_USERNAME}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
|
||||||
|
|
||||||
|
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
|
||||||
|
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Generate form POST request"
|
||||||
|
_post_request="$(_mktemp)"
|
||||||
|
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
|
||||||
|
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
|
||||||
|
_CERTPASSWORD_=
|
||||||
|
{
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
|
||||||
|
printf "Content-Type: application/octet-stream\r\n\r\n"
|
||||||
|
cat "${_ckey}" "${_cfullchain}"
|
||||||
|
printf "\r\n"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s--" "${_post_boundary}"
|
||||||
|
} >>"${_post_request}"
|
||||||
|
|
||||||
|
_info "Upload certificate to the FRITZ!Box"
|
||||||
|
|
||||||
|
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
|
||||||
|
_post "$(cat "${_post_request}")" "${DEPLOY_FRITZBOX_URL}/cgi-bin/firmwarecfg" | grep SSL
|
||||||
|
|
||||||
|
retval=$?
|
||||||
|
if [ $retval = 0 ]; then
|
||||||
|
_info "Upload successful"
|
||||||
|
else
|
||||||
|
_err "Upload failed"
|
||||||
|
fi
|
||||||
|
rm "${_post_request}"
|
||||||
|
|
||||||
|
return $retval
|
||||||
|
}
|
||||||
142
deploy/gcore_cdn.sh
Normal file
142
deploy/gcore_cdn.sh
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/).
|
||||||
|
# Returns 0 when success.
|
||||||
|
#
|
||||||
|
# Written by temoffey <temofffey@gmail.com>
|
||||||
|
# Public domain, 2019
|
||||||
|
|
||||||
|
#export DEPLOY_GCORE_CDN_USERNAME=myusername
|
||||||
|
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
|
||||||
|
gcore_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"
|
||||||
|
|
||||||
|
_fullchain=$(tr '\r\n' '*#' <"$_cfullchain" | sed 's/*#/#/g;s/##/#/g;s/#/\\n/g')
|
||||||
|
_key=$(tr '\r\n' '*#' <"$_ckey" | sed 's/*#/#/g;s/#/\\n/g')
|
||||||
|
|
||||||
|
_debug _fullchain "$_fullchain"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_GCORE_CDN_USERNAME" ]; then
|
||||||
|
if [ -z "$Le_Deploy_gcore_cdn_username" ]; then
|
||||||
|
_err "Please define the target username: export DEPLOY_GCORE_CDN_USERNAME=username"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_gcore_cdn_username="$DEPLOY_GCORE_CDN_USERNAME"
|
||||||
|
_savedomainconf Le_Deploy_gcore_cdn_username "$Le_Deploy_gcore_cdn_username"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_GCORE_CDN_PASSWORD" ]; then
|
||||||
|
if [ -z "$Le_Deploy_gcore_cdn_password" ]; then
|
||||||
|
_err "Please define the target password: export DEPLOY_GCORE_CDN_PASSWORD=password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_gcore_cdn_password="$DEPLOY_GCORE_CDN_PASSWORD"
|
||||||
|
_savedomainconf Le_Deploy_gcore_cdn_password "$Le_Deploy_gcore_cdn_password"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Get authorization token"
|
||||||
|
_request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
|
||||||
|
_debug _request "$_request"
|
||||||
|
export _H1="Content-Type:application/json"
|
||||||
|
_response=$(_post "$_request" "https://api.gcdn.co/auth/jwt/login")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _token "$_token"
|
||||||
|
|
||||||
|
if [ -z "$_token" ]; then
|
||||||
|
_err "Error G-Core Labs API authorization"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Find CDN resource with cname $_cdomain"
|
||||||
|
export _H2="Authorization:Token $_token"
|
||||||
|
_response=$(_get "https://api.gcdn.co/resources")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex="\"primary_resource\":null},"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_response=$(echo "$_response" | sed "s/$_regex/$_regex\n/g")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex="^.*\"cname\":\"$_cdomain\".*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_resource=$(echo "$_response" | _egrep_o "$_regex")
|
||||||
|
_debug _resource "$_resource"
|
||||||
|
_regex=".*\"id\":\([0-9]*\).*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _resourceId "$_resourceId"
|
||||||
|
_regex=".*\"sslData\":\([0-9]*\).*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _sslDataOld "$_sslDataOld"
|
||||||
|
_regex=".*\"originGroup\":\([0-9]*\).*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _originGroup "$_originGroup"
|
||||||
|
|
||||||
|
if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then
|
||||||
|
_err "Not found CDN resource with cname $_cdomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Add new SSL certificate"
|
||||||
|
_date=$(date "+%d.%m.%Y %H:%M:%S")
|
||||||
|
_request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
|
||||||
|
_debug _request "$_request"
|
||||||
|
_response=$(_post "$_request" "https://api.gcdn.co/sslData")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex=".*\"id\":\([0-9]*\).*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _sslDataAdd "$_sslDataAdd"
|
||||||
|
|
||||||
|
if [ -z "$_sslDataAdd" ]; then
|
||||||
|
_err "Error new SSL certificate add"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Update CDN resource"
|
||||||
|
_request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
|
||||||
|
_debug _request "$_request"
|
||||||
|
_response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex=".*\"sslData\":\([0-9]*\).*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
||||||
|
_debug _sslDataNew "$_sslDataNew"
|
||||||
|
|
||||||
|
if [ "$_sslDataNew" != "$_sslDataAdd" ]; then
|
||||||
|
_err "Error CDN resource update"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_sslDataOld" ] || [ "$_sslDataOld" = "null" ]; then
|
||||||
|
_info "Not found old SSL certificate"
|
||||||
|
else
|
||||||
|
_info "Delete old SSL certificate"
|
||||||
|
_response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE")
|
||||||
|
_debug _response "$_response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
80
deploy/gitlab.sh
Normal file
80
deploy/gitlab.sh
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificate to a Gitlab hosted page
|
||||||
|
|
||||||
|
# The following variables exported from environment will be used.
|
||||||
|
# If not set then values previously saved in domain.conf file are used.
|
||||||
|
|
||||||
|
# All the variables are required
|
||||||
|
|
||||||
|
# export GITLAB_TOKEN="xxxxxxx"
|
||||||
|
# export GITLAB_PROJECT_ID=012345
|
||||||
|
# export GITLAB_DOMAIN="mydomain.com"
|
||||||
|
|
||||||
|
gitlab_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 "$GITLAB_TOKEN" ]; then
|
||||||
|
if [ -z "$Le_Deploy_gitlab_token" ]; then
|
||||||
|
_err "GITLAB_TOKEN not defined."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_gitlab_token="$GITLAB_TOKEN"
|
||||||
|
_savedomainconf Le_Deploy_gitlab_token "$Le_Deploy_gitlab_token"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$GITLAB_PROJECT_ID" ]; then
|
||||||
|
if [ -z "$Le_Deploy_gitlab_project_id" ]; then
|
||||||
|
_err "GITLAB_PROJECT_ID not defined."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_gitlab_project_id="$GITLAB_PROJECT_ID"
|
||||||
|
_savedomainconf Le_Deploy_gitlab_project_id "$Le_Deploy_gitlab_project_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$GITLAB_DOMAIN" ]; then
|
||||||
|
if [ -z "$Le_Deploy_gitlab_domain" ]; then
|
||||||
|
_err "GITLAB_DOMAIN not defined."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_gitlab_domain="$GITLAB_DOMAIN"
|
||||||
|
_savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
string_fullchain=$(_url_encode <"$_cfullchain")
|
||||||
|
string_key=$(_url_encode <"$_ckey")
|
||||||
|
|
||||||
|
body="certificate=$string_fullchain&key=$string_key"
|
||||||
|
|
||||||
|
export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token"
|
||||||
|
|
||||||
|
gitlab_url="https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain"
|
||||||
|
|
||||||
|
_response=$(_post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline")
|
||||||
|
|
||||||
|
error_response="error"
|
||||||
|
|
||||||
|
if test "${_response#*$error_response}" != "$_response"; then
|
||||||
|
_err "Error in deploying certificate:"
|
||||||
|
_err "$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug response "$_response"
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
280
deploy/haproxy.sh
Normal file
280
deploy/haproxy.sh
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script for acme.sh to deploy certificates to haproxy
|
||||||
|
#
|
||||||
|
# The following variables can be exported:
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem"
|
||||||
|
#
|
||||||
|
# Defines the name of the PEM file.
|
||||||
|
# Defaults to "<domain>.pem"
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy"
|
||||||
|
#
|
||||||
|
# Defines location of PEM file for HAProxy.
|
||||||
|
# Defaults to /etc/haproxy
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Reload command used post deploy
|
||||||
|
# This defaults to be a no-op (ie "true").
|
||||||
|
# It is strongly recommended to set this something that makes sense
|
||||||
|
# for your distro.
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_ISSUER="no"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer"
|
||||||
|
# Note: Required for OCSP stapling to work
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_BUNDLE="no"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
|
||||||
|
# This adds a suffix to the certificate based on the certificate type
|
||||||
|
# eg RSA certificates will have .rsa as a suffix to the file name
|
||||||
|
# HAProxy will load all certificates and provide one or the other
|
||||||
|
# depending on client capabilities
|
||||||
|
# Note: This functionality requires HAProxy was compiled against
|
||||||
|
# a version of OpenSSL that supports this.
|
||||||
|
#
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
haproxy_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
# Some defaults
|
||||||
|
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
|
||||||
|
DEPLOY_HAPROXY_PEM_NAME_DEFAULT="${_cdomain}.pem"
|
||||||
|
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
|
||||||
|
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
|
||||||
|
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
|
||||||
|
|
||||||
|
_debug _cdomain "${_cdomain}"
|
||||||
|
_debug _ckey "${_ckey}"
|
||||||
|
_debug _ccert "${_ccert}"
|
||||||
|
_debug _cca "${_cca}"
|
||||||
|
_debug _cfullchain "${_cfullchain}"
|
||||||
|
|
||||||
|
# PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_PEM_PATH
|
||||||
|
_debug2 DEPLOY_HAPROXY_PEM_PATH "${DEPLOY_HAPROXY_PEM_PATH}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
|
||||||
|
Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then
|
||||||
|
Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure PEM_PATH exists
|
||||||
|
if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then
|
||||||
|
_debug "PEM_PATH ${Le_Deploy_haproxy_pem_path} exists"
|
||||||
|
else
|
||||||
|
_err "PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_PEM_NAME
|
||||||
|
_debug2 DEPLOY_HAPROXY_PEM_NAME "${DEPLOY_HAPROXY_PEM_NAME}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
|
||||||
|
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
|
||||||
|
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_BUNDLE
|
||||||
|
_debug2 DEPLOY_HAPROXY_BUNDLE "${DEPLOY_HAPROXY_BUNDLE}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
|
||||||
|
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then
|
||||||
|
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_ISSUER
|
||||||
|
_debug2 DEPLOY_HAPROXY_ISSUER "${DEPLOY_HAPROXY_ISSUER}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
|
||||||
|
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then
|
||||||
|
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_RELOAD
|
||||||
|
_debug2 DEPLOY_HAPROXY_RELOAD "${DEPLOY_HAPROXY_RELOAD}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
|
||||||
|
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_reload}" ]; then
|
||||||
|
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the suffix depending if we are creating a bundle or not
|
||||||
|
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
||||||
|
_info "Bundle creation requested"
|
||||||
|
# Initialise $Le_Keylength if its not already set
|
||||||
|
if [ -z "${Le_Keylength}" ]; then
|
||||||
|
Le_Keylength=""
|
||||||
|
fi
|
||||||
|
if _isEccKey "${Le_Keylength}"; then
|
||||||
|
_info "ECC key type detected"
|
||||||
|
_suffix=".ecdsa"
|
||||||
|
else
|
||||||
|
_info "RSA key type detected"
|
||||||
|
_suffix=".rsa"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_suffix=""
|
||||||
|
fi
|
||||||
|
_debug _suffix "${_suffix}"
|
||||||
|
|
||||||
|
# Set variables for later
|
||||||
|
_pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}"
|
||||||
|
_issuer="${_pem}.issuer"
|
||||||
|
_ocsp="${_pem}.ocsp"
|
||||||
|
_reload="${Le_Deploy_haproxy_reload}"
|
||||||
|
|
||||||
|
_info "Deploying PEM file"
|
||||||
|
# Create a temporary PEM file
|
||||||
|
_temppem="$(_mktemp)"
|
||||||
|
_debug _temppem "${_temppem}"
|
||||||
|
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
# Check that we could create the temporary file
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned during PEM file creation"
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move PEM file into place
|
||||||
|
_info "Moving new certificate into place"
|
||||||
|
_debug _pem "${_pem}"
|
||||||
|
cat "${_temppem}" >"${_pem}"
|
||||||
|
_ret=$?
|
||||||
|
|
||||||
|
# Clean up temp file
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
|
||||||
|
# Deal with any failure of moving PEM file into place
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while moving new certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .issuer file if requested
|
||||||
|
if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then
|
||||||
|
_info "Updating .issuer file"
|
||||||
|
_debug _issuer "${_issuer}"
|
||||||
|
cat "${_cca}" >"${_issuer}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while copying issuer/CA certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
|
||||||
|
if [ -z "${Le_OCSP_Staple}" ]; then
|
||||||
|
Le_OCSP_Staple="0"
|
||||||
|
fi
|
||||||
|
if [ "${Le_OCSP_Staple}" = "1" ]; then
|
||||||
|
_info "Updating OCSP stapling info"
|
||||||
|
_debug _ocsp "${_ocsp}"
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
|
||||||
|
_debug _ocsp_url "${_ocsp_url}"
|
||||||
|
|
||||||
|
# Only process OCSP if URL was present
|
||||||
|
if [ "${_ocsp_url}" != "" ]; then
|
||||||
|
# Extract the hostname from the OCSP URL
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
|
||||||
|
_debug _ocsp_host "${_ocsp_host}"
|
||||||
|
|
||||||
|
# Only process the certificate if we have a .issuer file
|
||||||
|
if [ -r "${_issuer}" ]; then
|
||||||
|
# Check if issuer cert is also a root CA cert
|
||||||
|
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _subjectdn "${_subjectdn}"
|
||||||
|
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _issuerdn "${_issuerdn}"
|
||||||
|
_info "Requesting OCSP response"
|
||||||
|
# If the issuer is a CA cert then our command line has "-CAfile" added
|
||||||
|
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
|
||||||
|
_cafile_argument="-CAfile \"${_issuer}\""
|
||||||
|
else
|
||||||
|
_cafile_argument=""
|
||||||
|
fi
|
||||||
|
_debug _cafile_argument "${_cafile_argument}"
|
||||||
|
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
|
||||||
|
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
|
||||||
|
_debug _openssl_version "${_openssl_version}"
|
||||||
|
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
|
||||||
|
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
|
||||||
|
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
|
||||||
|
_header_sep="="
|
||||||
|
else
|
||||||
|
_header_sep=" "
|
||||||
|
fi
|
||||||
|
# Request the OCSP response from the issuer and store it
|
||||||
|
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
|
||||||
|
-issuer \"${_issuer}\" \
|
||||||
|
-cert \"${_pem}\" \
|
||||||
|
-url \"${_ocsp_url}\" \
|
||||||
|
-header Host${_header_sep}\"${_ocsp_host}\" \
|
||||||
|
-respout \"${_ocsp}\" \
|
||||||
|
-verify_other \"${_issuer}\" \
|
||||||
|
${_cafile_argument} \
|
||||||
|
| grep -q \"${_pem}: good\""
|
||||||
|
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
|
||||||
|
eval "${_openssl_ocsp_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
else
|
||||||
|
# Non fatal: No issuer file was present so no OCSP stapling file created
|
||||||
|
_err "OCSP stapling in use but no .issuer file was present"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Non fatal: No OCSP url was found int the certificate
|
||||||
|
_err "OCSP update requested but no OCSP URL was found in certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Non fatal: Check return code of openssl command
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Updating OCSP stapling failed with return code ${_ret}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# An OCSP file was already present but certificate did not have OCSP extension
|
||||||
|
if [ -f "${_ocsp}" ]; then
|
||||||
|
_err "OCSP was not requested but .ocsp file exists."
|
||||||
|
# Could remove the file at this step, although HAProxy just ignores it in this case
|
||||||
|
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
25
deploy/keychain.sh
Normal file
25
deploy/keychain.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
keychain_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
/usr/bin/security import "$_ckey" -k "/Library/Keychains/System.keychain"
|
||||||
|
/usr/bin/security import "$_ccert" -k "/Library/Keychains/System.keychain"
|
||||||
|
/usr/bin/security import "$_cca" -k "/Library/Keychains/System.keychain"
|
||||||
|
/usr/bin/security import "$_cfullchain" -k "/Library/Keychains/System.keychain"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
77
deploy/kong.sh
Executable file
77
deploy/kong.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# If certificate already exists it will update only cert and key, not touching other parameters
|
||||||
|
# If certificate doesn't exist it will only upload cert and key, and not set other parameters
|
||||||
|
# Note that we deploy full chain
|
||||||
|
# Written by Geoffroi Genot <ggenot@voxbone.com>
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
kong_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_info "Deploying certificate on Kong instance"
|
||||||
|
if [ -z "$KONG_URL" ]; then
|
||||||
|
_debug "KONG_URL Not set, using default http://localhost:8001"
|
||||||
|
KONG_URL="http://localhost:8001"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
#Get ssl_uuid linked to the domain
|
||||||
|
ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
||||||
|
if [ -z "$ssl_uuid" ]; then
|
||||||
|
_debug "Unable to get Kong ssl_uuid for domain $_cdomain"
|
||||||
|
_debug "Make sure that KONG_URL is correctly configured"
|
||||||
|
_debug "Make sure that a Kong certificate match the sni"
|
||||||
|
_debug "Kong url: $KONG_URL"
|
||||||
|
_info "No existing certificate, creating..."
|
||||||
|
#return 1
|
||||||
|
fi
|
||||||
|
#Save kong url if it's succesful (First run case)
|
||||||
|
_saveaccountconf KONG_URL "$KONG_URL"
|
||||||
|
#Generate DEIM
|
||||||
|
delim="-----MultipartDelimiter$(date "+%s%N")"
|
||||||
|
nl="\015\012"
|
||||||
|
#Set Header
|
||||||
|
_H1="Content-Type: multipart/form-data; boundary=$delim"
|
||||||
|
#Generate data for request (Multipart/form-data with mixed content)
|
||||||
|
if [ -z "$ssl_uuid" ]; then
|
||||||
|
#set sni to domain
|
||||||
|
content="--$delim${nl}Content-Disposition: form-data; name=\"snis[]\"${nl}${nl}$_cdomain"
|
||||||
|
fi
|
||||||
|
#add key
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
||||||
|
#Add cert
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
||||||
|
#Close multipart
|
||||||
|
content="$content${nl}--$delim--${nl}"
|
||||||
|
#Convert CRLF
|
||||||
|
content=$(printf %b "$content")
|
||||||
|
#DEBUG
|
||||||
|
_debug header "$_H1"
|
||||||
|
_debug content "$content"
|
||||||
|
#Check if sslcreated (if not => POST else => PATCH)
|
||||||
|
|
||||||
|
if [ -z "$ssl_uuid" ]; then
|
||||||
|
#Post certificate to Kong
|
||||||
|
response=$(_post "$content" "$KONG_URL/certificates" "" "POST")
|
||||||
|
else
|
||||||
|
#patch
|
||||||
|
response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH")
|
||||||
|
fi
|
||||||
|
if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then
|
||||||
|
_err "An error occurred with cert upload. Check response:"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
}
|
||||||
280
deploy/lighttpd.sh
Normal file
280
deploy/lighttpd.sh
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script for acme.sh to deploy certificates to lighttpd
|
||||||
|
#
|
||||||
|
# The following variables can be exported:
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_PEM_NAME="${domain}.pem"
|
||||||
|
#
|
||||||
|
# Defines the name of the PEM file.
|
||||||
|
# Defaults to "<domain>.pem"
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_PEM_PATH="/etc/lighttpd"
|
||||||
|
#
|
||||||
|
# Defines location of PEM file for Lighttpd.
|
||||||
|
# Defaults to /etc/lighttpd
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_RELOAD="systemctl reload lighttpd"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Reload command used post deploy
|
||||||
|
# This defaults to be a no-op (ie "true").
|
||||||
|
# It is strongly recommended to set this something that makes sense
|
||||||
|
# for your distro.
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_ISSUER="yes"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Places CA file as "${DEPLOY_LIGHTTPD_PEM}.issuer"
|
||||||
|
# Note: Required for OCSP stapling to work
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_BUNDLE="no"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
|
||||||
|
# This adds a suffix to the certificate based on the certificate type
|
||||||
|
# eg RSA certificates will have .rsa as a suffix to the file name
|
||||||
|
# Lighttpd will load all certificates and provide one or the other
|
||||||
|
# depending on client capabilities
|
||||||
|
# Note: This functionality requires Lighttpd was compiled against
|
||||||
|
# a version of OpenSSL that supports this.
|
||||||
|
#
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
lighttpd_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
# Some defaults
|
||||||
|
DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT="/etc/lighttpd"
|
||||||
|
DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT="${_cdomain}.pem"
|
||||||
|
DEPLOY_LIGHTTPD_BUNDLE_DEFAULT="no"
|
||||||
|
DEPLOY_LIGHTTPD_ISSUER_DEFAULT="yes"
|
||||||
|
DEPLOY_LIGHTTPD_RELOAD_DEFAULT="true"
|
||||||
|
|
||||||
|
_debug _cdomain "${_cdomain}"
|
||||||
|
_debug _ckey "${_ckey}"
|
||||||
|
_debug _ccert "${_ccert}"
|
||||||
|
_debug _cca "${_cca}"
|
||||||
|
_debug _cfullchain "${_cfullchain}"
|
||||||
|
|
||||||
|
# PEM_PATH is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_PATH
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_PATH "${DEPLOY_LIGHTTPD_PEM_PATH}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_PATH}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_pem_path "${Le_Deploy_lighttpd_pem_path}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_path}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure PEM_PATH exists
|
||||||
|
if [ -d "${Le_Deploy_lighttpd_pem_path}" ]; then
|
||||||
|
_debug "PEM_PATH ${Le_Deploy_lighttpd_pem_path} exists"
|
||||||
|
else
|
||||||
|
_err "PEM_PATH ${Le_Deploy_lighttpd_pem_path} does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PEM_NAME is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_NAME
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_NAME "${DEPLOY_LIGHTTPD_PEM_NAME}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_NAME}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_pem_name "${Le_Deploy_lighttpd_pem_name}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_name}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_BUNDLE
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_BUNDLE "${DEPLOY_LIGHTTPD_BUNDLE}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_BUNDLE}" ]; then
|
||||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_bundle "${Le_Deploy_lighttpd_bundle}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_bundle}" ]; then
|
||||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ISSUER is optional. If not provided then assume "${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_ISSUER
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_ISSUER "${DEPLOY_LIGHTTPD_ISSUER}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_ISSUER}" ]; then
|
||||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_issuer "${Le_Deploy_lighttpd_issuer}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_issuer}" ]; then
|
||||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# RELOAD is optional. If not provided then assume "${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_RELOAD
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_RELOAD "${DEPLOY_LIGHTTPD_RELOAD}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_RELOAD}" ]; then
|
||||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_reload "${Le_Deploy_lighttpd_reload}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_reload}" ]; then
|
||||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the suffix depending if we are creating a bundle or not
|
||||||
|
if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then
|
||||||
|
_info "Bundle creation requested"
|
||||||
|
# Initialise $Le_Keylength if its not already set
|
||||||
|
if [ -z "${Le_Keylength}" ]; then
|
||||||
|
Le_Keylength=""
|
||||||
|
fi
|
||||||
|
if _isEccKey "${Le_Keylength}"; then
|
||||||
|
_info "ECC key type detected"
|
||||||
|
_suffix=".ecdsa"
|
||||||
|
else
|
||||||
|
_info "RSA key type detected"
|
||||||
|
_suffix=".rsa"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_suffix=""
|
||||||
|
fi
|
||||||
|
_debug _suffix "${_suffix}"
|
||||||
|
|
||||||
|
# Set variables for later
|
||||||
|
_pem="${Le_Deploy_lighttpd_pem_path}/${Le_Deploy_lighttpd_pem_name}${_suffix}"
|
||||||
|
_issuer="${_pem}.issuer"
|
||||||
|
_ocsp="${_pem}.ocsp"
|
||||||
|
_reload="${Le_Deploy_lighttpd_reload}"
|
||||||
|
|
||||||
|
_info "Deploying PEM file"
|
||||||
|
# Create a temporary PEM file
|
||||||
|
_temppem="$(_mktemp)"
|
||||||
|
_debug _temppem "${_temppem}"
|
||||||
|
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
# Check that we could create the temporary file
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned during PEM file creation"
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move PEM file into place
|
||||||
|
_info "Moving new certificate into place"
|
||||||
|
_debug _pem "${_pem}"
|
||||||
|
cat "${_temppem}" >"${_pem}"
|
||||||
|
_ret=$?
|
||||||
|
|
||||||
|
# Clean up temp file
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
|
||||||
|
# Deal with any failure of moving PEM file into place
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while moving new certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .issuer file if requested
|
||||||
|
if [ "${Le_Deploy_lighttpd_issuer}" = "yes" ]; then
|
||||||
|
_info "Updating .issuer file"
|
||||||
|
_debug _issuer "${_issuer}"
|
||||||
|
cat "${_cca}" >"${_issuer}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while copying issuer/CA certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
|
||||||
|
if [ -z "${Le_OCSP_Staple}" ]; then
|
||||||
|
Le_OCSP_Staple="0"
|
||||||
|
fi
|
||||||
|
if [ "${Le_OCSP_Staple}" = "1" ]; then
|
||||||
|
_info "Updating OCSP stapling info"
|
||||||
|
_debug _ocsp "${_ocsp}"
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
|
||||||
|
_debug _ocsp_url "${_ocsp_url}"
|
||||||
|
|
||||||
|
# Only process OCSP if URL was present
|
||||||
|
if [ "${_ocsp_url}" != "" ]; then
|
||||||
|
# Extract the hostname from the OCSP URL
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
|
||||||
|
_debug _ocsp_host "${_ocsp_host}"
|
||||||
|
|
||||||
|
# Only process the certificate if we have a .issuer file
|
||||||
|
if [ -r "${_issuer}" ]; then
|
||||||
|
# Check if issuer cert is also a root CA cert
|
||||||
|
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _subjectdn "${_subjectdn}"
|
||||||
|
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _issuerdn "${_issuerdn}"
|
||||||
|
_info "Requesting OCSP response"
|
||||||
|
# If the issuer is a CA cert then our command line has "-CAfile" added
|
||||||
|
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
|
||||||
|
_cafile_argument="-CAfile \"${_issuer}\""
|
||||||
|
else
|
||||||
|
_cafile_argument=""
|
||||||
|
fi
|
||||||
|
_debug _cafile_argument "${_cafile_argument}"
|
||||||
|
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
|
||||||
|
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
|
||||||
|
_debug _openssl_version "${_openssl_version}"
|
||||||
|
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
|
||||||
|
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
|
||||||
|
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
|
||||||
|
_header_sep="="
|
||||||
|
else
|
||||||
|
_header_sep=" "
|
||||||
|
fi
|
||||||
|
# Request the OCSP response from the issuer and store it
|
||||||
|
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
|
||||||
|
-issuer \"${_issuer}\" \
|
||||||
|
-cert \"${_pem}\" \
|
||||||
|
-url \"${_ocsp_url}\" \
|
||||||
|
-header Host${_header_sep}\"${_ocsp_host}\" \
|
||||||
|
-respout \"${_ocsp}\" \
|
||||||
|
-verify_other \"${_issuer}\" \
|
||||||
|
${_cafile_argument} \
|
||||||
|
| grep -q \"${_pem}: good\""
|
||||||
|
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
|
||||||
|
eval "${_openssl_ocsp_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
else
|
||||||
|
# Non fatal: No issuer file was present so no OCSP stapling file created
|
||||||
|
_err "OCSP stapling in use but no .issuer file was present"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Non fatal: No OCSP url was found int the certificate
|
||||||
|
_err "OCSP update requested but no OCSP URL was found in certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Non fatal: Check return code of openssl command
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Updating OCSP stapling failed with return code ${_ret}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# An OCSP file was already present but certificate did not have OCSP extension
|
||||||
|
if [ -f "${_ocsp}" ]; then
|
||||||
|
_err "OCSP was not requested but .ocsp file exists."
|
||||||
|
# Could remove the file at this step, although Lighttpd just ignores it in this case
|
||||||
|
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload Lighttpd
|
||||||
|
_debug _reload "${_reload}"
|
||||||
|
eval "${_reload}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} during reload"
|
||||||
|
return ${_ret}
|
||||||
|
else
|
||||||
|
_info "Reload successful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
75
deploy/mailcow.sh
Normal file
75
deploy/mailcow.sh
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to mailcow.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
mailcow_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"
|
||||||
|
|
||||||
|
_mailcow_path="${DEPLOY_MAILCOW_PATH}"
|
||||||
|
|
||||||
|
if [ -z "$_mailcow_path" ]; then
|
||||||
|
_err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Tests if _ssl_path is the mailcow root directory.
|
||||||
|
if [ -f "${_mailcow_path}/generate_config.sh" ]; then
|
||||||
|
_ssl_path="${_mailcow_path}/data/assets/ssl/"
|
||||||
|
else
|
||||||
|
_ssl_path="${_mailcow_path}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$_ssl_path" ]; then
|
||||||
|
_err "Cannot find mailcow ssl path: $_ssl_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ECC or RSA
|
||||||
|
if [ -z "${Le_Keylength}" ]; then
|
||||||
|
Le_Keylength=""
|
||||||
|
fi
|
||||||
|
if _isEccKey "${Le_Keylength}"; then
|
||||||
|
_info "ECC key type detected"
|
||||||
|
_cert_name_prefix="ecdsa-"
|
||||||
|
else
|
||||||
|
_info "RSA key type detected"
|
||||||
|
_cert_name_prefix=""
|
||||||
|
fi
|
||||||
|
_info "Copying key and cert"
|
||||||
|
_real_key="$_ssl_path/${_cert_name_prefix}key.pem"
|
||||||
|
if ! cat "$_ckey" >"$_real_key"; then
|
||||||
|
_err "Error: write key file to: $_real_key"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_real_fullchain="$_ssl_path/${_cert_name_prefix}cert.pem"
|
||||||
|
if ! cat "$_cfullchain" >"$_real_fullchain"; then
|
||||||
|
_err "Error: write cert file to: $_real_fullchain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEFAULT_MAILCOW_RELOAD="docker restart $(docker ps -qaf name=postfix-mailcow); docker restart $(docker ps -qaf name=nginx-mailcow); docker restart $(docker ps -qaf name=dovecot-mailcow)"
|
||||||
|
_reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
|
||||||
|
|
||||||
|
_info "Run reload: $_reload"
|
||||||
|
if eval "$_reload"; then
|
||||||
|
_info "Reload success!"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
28
deploy/myapi.sh
Executable file
28
deploy/myapi.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a sample custom api script.
|
||||||
|
#This file name is "myapi.sh"
|
||||||
|
#So, here must be a method myapi_deploy()
|
||||||
|
#Which will be called by acme.sh to deploy the cert
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
myapi_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
59
deploy/mydevil.sh
Executable file
59
deploy/mydevil.sh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# MyDevil.net API (2019-02-03)
|
||||||
|
#
|
||||||
|
# MyDevil.net already supports automatic Let's Encrypt certificates,
|
||||||
|
# except for wildcard domains.
|
||||||
|
#
|
||||||
|
# This script depends on `devil` command that MyDevil.net provides,
|
||||||
|
# which means that it works only on server side.
|
||||||
|
#
|
||||||
|
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: mydevil_deploy domain keyfile certfile cafile fullchain
|
||||||
|
mydevil_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
ip=""
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if ! _exists "devil"; then
|
||||||
|
_err "Could not find 'devil' command."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ip=$(mydevil_get_ip "$_cdomain")
|
||||||
|
if [ -z "$ip" ]; then
|
||||||
|
_err "Could not find IP for domain $_cdomain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete old certificate first
|
||||||
|
_info "Removing old certificate for $_cdomain at $ip"
|
||||||
|
devil ssl www del "$ip" "$_cdomain"
|
||||||
|
|
||||||
|
# Add new certificate
|
||||||
|
_info "Adding new certificate for $_cdomain at $ip"
|
||||||
|
devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# Usage: ip=$(mydevil_get_ip domain.com)
|
||||||
|
# echo $ip
|
||||||
|
mydevil_get_ip() {
|
||||||
|
devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
26
deploy/mysqld.sh
Normal file
26
deploy/mysqld.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to mysqld server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
mysqld_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "deploy cert to mysqld server, Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
26
deploy/nginx.sh
Normal file
26
deploy/nginx.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to nginx server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
nginx_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "deploy cert to nginx server, Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
156
deploy/openmediavault.sh
Normal file
156
deploy/openmediavault.sh
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# This deploy hook is tested on OpenMediaVault 5.x. It supports both local and remote deployment.
|
||||||
|
# The way it works is that if a cert with the matching domain name is not found, it will firstly create a dummy cert to get its uuid, and then replace it with your cert.
|
||||||
|
#
|
||||||
|
# DEPLOY_OMV_WEBUI_ADMIN - This is OMV web gui admin account. Default value is admin. It's required as the user parameter (-u) for the omv-rpc command.
|
||||||
|
# DEPLOY_OMV_HOST and DEPLOY_OMV_SSH_USER are optional. They are used for remote deployment through ssh (support public key authentication only). Per design, OMV web gui admin doesn't have ssh permission, so another account is needed for ssh.
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
openmediavault_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_OMV_WEBUI_ADMIN
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_OMV_WEBUI_ADMIN" ]; then
|
||||||
|
DEPLOY_OMV_WEBUI_ADMIN="admin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_OMV_WEBUI_ADMIN "$DEPLOY_OMV_WEBUI_ADMIN"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_OMV_HOST
|
||||||
|
_getdeployconf DEPLOY_OMV_SSH_USER
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
|
||||||
|
_info "[OMV deploy-hook] Deploy certificate remotely through ssh."
|
||||||
|
_savedeployconf DEPLOY_OMV_HOST "$DEPLOY_OMV_HOST"
|
||||||
|
_savedeployconf DEPLOY_OMV_SSH_USER "$DEPLOY_OMV_SSH_USER"
|
||||||
|
else
|
||||||
|
_info "[OMV deploy-hook] Deploy certificate locally."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{\"start\": 0, \"limit\": -1}' | jq -r '.data[] | select(.name==\"/CN='$_cdomain'\") | .uuid'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
_debug _command "$_command"
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{\"cn\": \"test.example.com\", \"size\": 4096, \"days\": 3650, \"c\": \"\", \"st\": \"\", \"l\": \"\", \"o\": \"\", \"ou\": \"\", \"email\": \"\"}' | jq -r '.uuid'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
_debug _command "$_command"
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_err "[OMV deploy-hook] An error occured while creating the certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
|
||||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .)
|
||||||
|
_key=$(jq <"$_ckey" -aRs .)
|
||||||
|
|
||||||
|
_debug _fullchain "$_fullchain"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking nginx to reload"
|
||||||
|
_command="nginx -s reload"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{"start": 0, "limit": -1}' | jq -r '.data[] | select(.name=="/CN='$_cdomain'") | .uuid')
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{"cn": "test.example.com", "size": 4096, "days": 3650, "c": "", "st": "", "l": "", "o": "", "ou": "", "email": ""}' | jq -r '.uuid')
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_err "[OMB deploy-hook] An error occured while creating the certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
|
||||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .)
|
||||||
|
_key=$(jq <"$_ckey" -aRs .)
|
||||||
|
|
||||||
|
_debug _fullchain "$_fullchain"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking nginx to reload"
|
||||||
|
_command="nginx -s reload"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
26
deploy/opensshd.sh
Normal file
26
deploy/opensshd.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to opensshd server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
opensshd_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "deploy cert to opensshd server, Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
262
deploy/openstack.sh
Normal file
262
deploy/openstack.sh
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# OpenStack Barbican deploy hook
|
||||||
|
#
|
||||||
|
# This requires you to have OpenStackClient and python-barbicanclient
|
||||||
|
# installed.
|
||||||
|
#
|
||||||
|
# You will require Keystone V3 credentials loaded into your environment, which
|
||||||
|
# could be either password or v3applicationcredential type.
|
||||||
|
#
|
||||||
|
# Author: Andy Botting <andy@andybotting.com>
|
||||||
|
|
||||||
|
openstack_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if ! _exists openstack; then
|
||||||
|
_err "OpenStack client not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_openstack_credentials || return $?
|
||||||
|
|
||||||
|
_info "Generate import pkcs12"
|
||||||
|
_import_pkcs12="$(_mktemp)"
|
||||||
|
if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then
|
||||||
|
_err "Error creating pkcs12 certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _import_pkcs12 "$_import_pkcs12"
|
||||||
|
_base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12")
|
||||||
|
|
||||||
|
secretHrefs=$(_openstack_get_secrets)
|
||||||
|
_debug secretHrefs "$secretHrefs"
|
||||||
|
_openstack_store_secret || return $?
|
||||||
|
|
||||||
|
if [ -n "$secretHrefs" ]; then
|
||||||
|
_info "Cleaning up existing secret"
|
||||||
|
_openstack_delete_secrets || return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_store_secret() {
|
||||||
|
if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then
|
||||||
|
_err "Failed to create OpenStack secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_delete_secrets() {
|
||||||
|
echo "$secretHrefs" | while read -r secretHref; do
|
||||||
|
_info "Deleting old secret $secretHref"
|
||||||
|
if ! openstack secret delete "$secretHref"; then
|
||||||
|
_err "Failed to delete OpenStack secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_get_secrets() {
|
||||||
|
if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then
|
||||||
|
_err "Failed to list secrets"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$secretHrefs"
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_to_pkcs() {
|
||||||
|
# The existing _toPkcs command can't allow an empty password, due to sh
|
||||||
|
# -z test, so copied here and forcing the empty password.
|
||||||
|
_cpfx="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
|
||||||
|
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:"
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_credentials() {
|
||||||
|
_debug "Check OpenStack credentials"
|
||||||
|
|
||||||
|
# If we have OS_AUTH_URL already set in the environment, then assume we want
|
||||||
|
# to use those, otherwise use stored credentials
|
||||||
|
if [ -n "$OS_AUTH_URL" ]; then
|
||||||
|
_debug "OS_AUTH_URL env var found, using environment"
|
||||||
|
else
|
||||||
|
_debug "OS_AUTH_URL not found, loading stored credentials"
|
||||||
|
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
|
||||||
|
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
|
||||||
|
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
|
||||||
|
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
|
||||||
|
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
|
||||||
|
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
|
||||||
|
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
|
||||||
|
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
|
||||||
|
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
|
||||||
|
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
|
||||||
|
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
|
||||||
|
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
|
||||||
|
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check each var and either save or clear it depending on whether its set.
|
||||||
|
# The helps us clear out old vars in the case where a user may want
|
||||||
|
# to switch between password and app creds
|
||||||
|
_debug "OS_AUTH_URL" "$OS_AUTH_URL"
|
||||||
|
if [ -n "$OS_AUTH_URL" ]; then
|
||||||
|
export OS_AUTH_URL
|
||||||
|
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
|
||||||
|
else
|
||||||
|
unset OS_AUTH_URL
|
||||||
|
_clearaccountconf SAVED_OS_AUTH_URL
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
|
||||||
|
if [ -n "$OS_IDENTITY_API_VERSION" ]; then
|
||||||
|
export OS_IDENTITY_API_VERSION
|
||||||
|
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
|
||||||
|
else
|
||||||
|
unset OS_IDENTITY_API_VERSION
|
||||||
|
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
|
||||||
|
if [ -n "$OS_AUTH_TYPE" ]; then
|
||||||
|
export OS_AUTH_TYPE
|
||||||
|
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
|
||||||
|
else
|
||||||
|
unset OS_AUTH_TYPE
|
||||||
|
_clearaccountconf SAVED_OS_AUTH_TYPE
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
|
||||||
|
export OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
else
|
||||||
|
unset OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
|
||||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
|
||||||
|
export OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
|
||||||
|
else
|
||||||
|
unset OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USERNAME" "$OS_USERNAME"
|
||||||
|
if [ -n "$OS_USERNAME" ]; then
|
||||||
|
export OS_USERNAME
|
||||||
|
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
|
||||||
|
else
|
||||||
|
unset OS_USERNAME
|
||||||
|
_clearaccountconf SAVED_OS_USERNAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "OS_PASSWORD" "$OS_PASSWORD"
|
||||||
|
if [ -n "$OS_PASSWORD" ]; then
|
||||||
|
export OS_PASSWORD
|
||||||
|
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
|
||||||
|
else
|
||||||
|
unset OS_PASSWORD
|
||||||
|
_clearaccountconf SAVED_OS_PASSWORD
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
|
||||||
|
if [ -n "$OS_PROJECT_NAME" ]; then
|
||||||
|
export OS_PROJECT_NAME
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_NAME
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
|
||||||
|
if [ -n "$OS_PROJECT_ID" ]; then
|
||||||
|
export OS_PROJECT_ID
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_ID
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
|
||||||
|
if [ -n "$OS_USER_DOMAIN_NAME" ]; then
|
||||||
|
export OS_USER_DOMAIN_NAME
|
||||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
|
||||||
|
else
|
||||||
|
unset OS_USER_DOMAIN_NAME
|
||||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
|
||||||
|
if [ -n "$OS_USER_DOMAIN_ID" ]; then
|
||||||
|
export OS_USER_DOMAIN_ID
|
||||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
|
||||||
|
else
|
||||||
|
unset OS_USER_DOMAIN_ID
|
||||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
|
||||||
|
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
|
||||||
|
export OS_PROJECT_DOMAIN_NAME
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_DOMAIN_NAME
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
|
||||||
|
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
|
||||||
|
export OS_PROJECT_DOMAIN_ID
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_DOMAIN_ID
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
|
||||||
|
# Application Credential auth
|
||||||
|
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
|
||||||
|
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Password auth
|
||||||
|
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
|
||||||
|
_err "OpenStack username or password not found."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
|
||||||
|
_err "When using password authentication, OS_PROJECT_NAME or"
|
||||||
|
_err "OS_PROJECT_ID must be set."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
139
deploy/panos.sh
Normal file
139
deploy/panos.sh
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificates to Palo Alto Networks PANOS via API
|
||||||
|
# Note PANOS API KEY and IP address needs to be set prior to running.
|
||||||
|
# The following variables exported from environment will be used.
|
||||||
|
# If not set then values previously saved in domain.conf file are used.
|
||||||
|
#
|
||||||
|
# Firewall admin with superuser and IP address is required.
|
||||||
|
#
|
||||||
|
# export PANOS_USER="" # required
|
||||||
|
# export PANOS_PASS="" # required
|
||||||
|
# export PANOS_HOST="" # required
|
||||||
|
|
||||||
|
# This function is to parse the XML
|
||||||
|
parse_response() {
|
||||||
|
type=$2
|
||||||
|
if [ "$type" = 'keygen' ]; then
|
||||||
|
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
|
||||||
|
if [ "$status" = "success" ]; then
|
||||||
|
panos_key=$(echo "$1" | sed 's/^.*\(<key>\)\(.*\)<\/key>.*/\2/g')
|
||||||
|
_panos_key=$panos_key
|
||||||
|
else
|
||||||
|
message="PAN-OS Key could not be set."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
|
||||||
|
message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
deployer() {
|
||||||
|
content=""
|
||||||
|
type=$1 # Types are keygen, cert, key, commit
|
||||||
|
_debug "**** Deploying $type *****"
|
||||||
|
panos_url="https://$_panos_host/api/"
|
||||||
|
if [ "$type" = 'keygen' ]; then
|
||||||
|
_H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
content="type=keygen&user=$_panos_user&password=$_panos_pass"
|
||||||
|
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
|
||||||
|
#Generate DEIM
|
||||||
|
delim="-----MultipartDelimiter$(date "+%s%N")"
|
||||||
|
nl="\015\012"
|
||||||
|
#Set Header
|
||||||
|
export _H1="Content-Type: multipart/form-data; boundary=$delim"
|
||||||
|
if [ "$type" = 'cert' ]; then
|
||||||
|
panos_url="${panos_url}?type=import"
|
||||||
|
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=\"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=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
||||||
|
fi
|
||||||
|
if [ "$type" = 'key' ]; then
|
||||||
|
panos_url="${panos_url}?type=import"
|
||||||
|
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=\"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=\"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")"
|
||||||
|
fi
|
||||||
|
#Close multipart
|
||||||
|
content="$content${nl}--$delim--${nl}${nl}"
|
||||||
|
#Convert CRLF
|
||||||
|
content=$(printf %b "$content")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$type" = 'commit' ]; then
|
||||||
|
export _H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
|
||||||
|
content="type=commit&key=$_panos_key&cmd=$cmd"
|
||||||
|
fi
|
||||||
|
response=$(_post "$content" "$panos_url" "" "POST")
|
||||||
|
parse_response "$response" "$type"
|
||||||
|
# Saving response to variables
|
||||||
|
response_status=$status
|
||||||
|
#DEBUG
|
||||||
|
_debug response_status "$response_status"
|
||||||
|
if [ "$response_status" = "success" ]; then
|
||||||
|
_debug "Successfully deployed $type"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
|
||||||
|
_debug "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is the main function that will call the other functions to deploy everything.
|
||||||
|
panos_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_cfullchain="$5"
|
||||||
|
# PANOS ENV VAR check
|
||||||
|
if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
|
||||||
|
_debug "No ENV variables found lets check for saved variables"
|
||||||
|
_getdeployconf PANOS_USER
|
||||||
|
_getdeployconf PANOS_PASS
|
||||||
|
_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
|
||||||
|
_debug "Let's use username and pass to generate token."
|
||||||
|
if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
|
||||||
|
_err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_debug "Getting PANOS KEY"
|
||||||
|
deployer keygen
|
||||||
|
if [ -z "$_panos_key" ]; then
|
||||||
|
_err "Missing apikey."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
deployer cert
|
||||||
|
deployer key
|
||||||
|
deployer commit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
123
deploy/peplink.sh
Normal file
123
deploy/peplink.sh
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy cert to Peplink Routers
|
||||||
|
#
|
||||||
|
# The following environment variables must be set:
|
||||||
|
#
|
||||||
|
# PEPLINK_Hostname - Peplink hostname
|
||||||
|
# PEPLINK_Username - Peplink username to login
|
||||||
|
# PEPLINK_Password - Peplink password to login
|
||||||
|
#
|
||||||
|
# The following environmental variables may be set if you don't like their
|
||||||
|
# default values:
|
||||||
|
#
|
||||||
|
# PEPLINK_Certtype - Certificate type to target for replacement
|
||||||
|
# defaults to "webadmin", can be one of:
|
||||||
|
# * "chub" (ContentHub)
|
||||||
|
# * "openvpn" (OpenVPN CA)
|
||||||
|
# * "portal" (Captive Portal SSL)
|
||||||
|
# * "webadmin" (Web Admin SSL)
|
||||||
|
# * "webproxy" (Proxy Root CA)
|
||||||
|
# * "wwan_ca" (Wi-Fi WAN CA)
|
||||||
|
# * "wwan_client" (Wi-Fi WAN Client)
|
||||||
|
# PEPLINK_Scheme - defaults to "https"
|
||||||
|
# PEPLINK_Port - defaults to "443"
|
||||||
|
#
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
_peplink_get_cookie_data() {
|
||||||
|
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
|
||||||
|
}
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
peplink_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
|
||||||
|
# Get Hostname, Username and Password, but don't save until we successfully authenticate
|
||||||
|
_getdeployconf PEPLINK_Hostname
|
||||||
|
_getdeployconf PEPLINK_Username
|
||||||
|
_getdeployconf PEPLINK_Password
|
||||||
|
if [ -z "${PEPLINK_Hostname:-}" ] || [ -z "${PEPLINK_Username:-}" ] || [ -z "${PEPLINK_Password:-}" ]; then
|
||||||
|
_err "PEPLINK_Hostname & PEPLINK_Username & PEPLINK_Password must be set"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 PEPLINK_Hostname "$PEPLINK_Hostname"
|
||||||
|
_debug2 PEPLINK_Username "$PEPLINK_Username"
|
||||||
|
_secure_debug2 PEPLINK_Password "$PEPLINK_Password"
|
||||||
|
|
||||||
|
# Optional certificate type, scheme, and port for Peplink
|
||||||
|
_getdeployconf PEPLINK_Certtype
|
||||||
|
_getdeployconf PEPLINK_Scheme
|
||||||
|
_getdeployconf PEPLINK_Port
|
||||||
|
|
||||||
|
# Don't save the certificate type until we verify it exists and is supported
|
||||||
|
_savedeployconf PEPLINK_Scheme "$PEPLINK_Scheme"
|
||||||
|
_savedeployconf PEPLINK_Port "$PEPLINK_Port"
|
||||||
|
|
||||||
|
# Default vaules for certificate type, scheme, and port
|
||||||
|
[ -n "${PEPLINK_Certtype}" ] || PEPLINK_Certtype="webadmin"
|
||||||
|
[ -n "${PEPLINK_Scheme}" ] || PEPLINK_Scheme="https"
|
||||||
|
[ -n "${PEPLINK_Port}" ] || PEPLINK_Port="443"
|
||||||
|
|
||||||
|
_debug2 PEPLINK_Certtype "$PEPLINK_Certtype"
|
||||||
|
_debug2 PEPLINK_Scheme "$PEPLINK_Scheme"
|
||||||
|
_debug2 PEPLINK_Port "$PEPLINK_Port"
|
||||||
|
|
||||||
|
_base_url="$PEPLINK_Scheme://$PEPLINK_Hostname:$PEPLINK_Port"
|
||||||
|
_debug _base_url "$_base_url"
|
||||||
|
|
||||||
|
# Login, get the auth token from the cookie
|
||||||
|
_info "Logging into $PEPLINK_Hostname:$PEPLINK_Port"
|
||||||
|
encoded_username="$(printf "%s" "$PEPLINK_Username" | _url_encode)"
|
||||||
|
encoded_password="$(printf "%s" "$PEPLINK_Password" | _url_encode)"
|
||||||
|
response=$(_post "func=login&username=$encoded_username&password=$encoded_password" "$_base_url/cgi-bin/MANGA/api.cgi")
|
||||||
|
auth_token=$(_peplink_get_cookie_data "bauth" <"$HTTP_HEADER")
|
||||||
|
_debug3 response "$response"
|
||||||
|
_debug auth_token "$auth_token"
|
||||||
|
|
||||||
|
if [ -z "$auth_token" ]; then
|
||||||
|
_err "Unable to authenticate to $PEPLINK_Hostname:$PEPLINK_Port using $PEPLINK_Scheme."
|
||||||
|
_err "Check your username and password."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_H1="Cookie: $auth_token"
|
||||||
|
export _H1
|
||||||
|
_debug2 H1 "${_H1}"
|
||||||
|
|
||||||
|
# Now that we know the hostnameusername and password are good, save them
|
||||||
|
_savedeployconf PEPLINK_Hostname "$PEPLINK_Hostname"
|
||||||
|
_savedeployconf PEPLINK_Username "$PEPLINK_Username"
|
||||||
|
_savedeployconf PEPLINK_Password "$PEPLINK_Password"
|
||||||
|
|
||||||
|
_info "Generate form POST request"
|
||||||
|
|
||||||
|
encoded_key="$(_url_encode <"$_ckey")"
|
||||||
|
encoded_fullchain="$(_url_encode <"$_cfullchain")"
|
||||||
|
body="cert_type=$PEPLINK_Certtype&cert_uid=§ion=CERT_modify&key_pem=$encoded_key&key_pem_passphrase=&key_pem_passphrase_confirm=&cert_pem=$encoded_fullchain"
|
||||||
|
_debug3 body "$body"
|
||||||
|
|
||||||
|
_info "Upload $PEPLINK_Certtype certificate to the Peplink"
|
||||||
|
|
||||||
|
response=$(_post "$body" "$_base_url/cgi-bin/MANGA/admin.cgi")
|
||||||
|
_debug3 response "$response"
|
||||||
|
|
||||||
|
if echo "$response" | grep 'Success' >/dev/null; then
|
||||||
|
# We've verified this certificate type is valid, so save it
|
||||||
|
_savedeployconf PEPLINK_Certtype "$PEPLINK_Certtype"
|
||||||
|
_info "Certificate was updated"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Unable to update certificate, error code $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
26
deploy/pureftpd.sh
Normal file
26
deploy/pureftpd.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to pureftpd server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
pureftpd_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_err "deploy cert to pureftpd server, Not implemented yet"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
96
deploy/qiniu.sh
Normal file
96
deploy/qiniu.sh
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to create certificate to qiniu.com
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export QINIU_AK="QINIUACCESSKEY"
|
||||||
|
# export QINIU_SK="QINIUSECRETKEY"
|
||||||
|
# export QINIU_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have more than one domain, just
|
||||||
|
# export QINIU_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
|
||||||
|
QINIU_API_BASE="https://api.qiniu.com"
|
||||||
|
|
||||||
|
qiniu_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 "$QINIU_AK" ]; then
|
||||||
|
_err "QINIU_AK is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf QINIU_AK "$QINIU_AK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$QINIU_SK" ]; then
|
||||||
|
_err "QINIU_SK is not defined."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_savedomainconf QINIU_SK "$QINIU_SK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$QINIU_CDN_DOMAIN" ]; then
|
||||||
|
_savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN"
|
||||||
|
else
|
||||||
|
QINIU_CDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## upload certificate
|
||||||
|
string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
|
||||||
|
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
|
||||||
|
|
||||||
|
sslcert_path="/sslcert"
|
||||||
|
sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$QINIU_CDN_DOMAIN\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}"
|
||||||
|
sslcert_access_token="$(_make_access_token "$sslcert_path")"
|
||||||
|
_debug sslcert_access_token "$sslcert_access_token"
|
||||||
|
export _H1="Authorization: QBox $sslcert_access_token"
|
||||||
|
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline")
|
||||||
|
|
||||||
|
if ! _contains "$sslcert_response" "certID"; then
|
||||||
|
_err "Error in creating certificate:"
|
||||||
|
_err "$sslcert_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug sslcert_response "$sslcert_response"
|
||||||
|
_info "Certificate successfully uploaded, updating domain $_cdomain"
|
||||||
|
|
||||||
|
## extract certId
|
||||||
|
_certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\": *\"[^\"]*\"" | cut -d : -f 2)"
|
||||||
|
_debug certId "$_certId"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
update_body="{\"certid\":$_certId,\"forceHttps\":false}"
|
||||||
|
for domain in $QINIU_CDN_DOMAIN; do
|
||||||
|
update_path="/domain/$domain/httpsconf"
|
||||||
|
update_access_token="$(_make_access_token "$update_path")"
|
||||||
|
_debug update_access_token "$update_access_token"
|
||||||
|
export _H1="Authorization: QBox $update_access_token"
|
||||||
|
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline")
|
||||||
|
|
||||||
|
if _contains "$update_response" "error"; then
|
||||||
|
_err "Error in updating domain $domain httpsconf:"
|
||||||
|
_err "$update_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug update_response "$update_response"
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_make_access_token() {
|
||||||
|
_token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64 | tr -- '+/' '-_')"
|
||||||
|
echo "$QINIU_AK:$_token"
|
||||||
|
}
|
||||||
121
deploy/routeros.sh
Normal file
121
deploy/routeros.sh
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to routeros router.
|
||||||
|
# Deploy the cert to remote routeros
|
||||||
|
#
|
||||||
|
# ```sh
|
||||||
|
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# Before you can deploy the certificate to router os, you need
|
||||||
|
# to add the id_rsa.pub key to the routeros and assign a user
|
||||||
|
# to that key.
|
||||||
|
#
|
||||||
|
# The user need to have access to ssh, ftp, read and write.
|
||||||
|
#
|
||||||
|
# There are no need to enable ftp service for the script to work,
|
||||||
|
# as they are transmitted over SCP, however ftp is needed to store
|
||||||
|
# the files on the router.
|
||||||
|
#
|
||||||
|
# Then you need to set the environment variables for the
|
||||||
|
# deploy script to work.
|
||||||
|
#
|
||||||
|
# ```sh
|
||||||
|
# export ROUTER_OS_USERNAME=certuser
|
||||||
|
# export ROUTER_OS_HOST=router.example.com
|
||||||
|
#
|
||||||
|
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# The deploy script will remove previously deployed certificates,
|
||||||
|
# and it does this with an assumption on how RouterOS names imported
|
||||||
|
# certificates, adding a "cer_0" suffix at the end. This is true for
|
||||||
|
# versions 6.32 -> 6.41.3, but it is not guaranteed that it will be
|
||||||
|
# true for future versions when upgrading.
|
||||||
|
#
|
||||||
|
# If the router have other certificates with the same name as the one
|
||||||
|
# beeing deployed, then this script will remove those certificates.
|
||||||
|
#
|
||||||
|
# At the end of the script, the services that use those certificates
|
||||||
|
# could be updated. Currently only the www-ssl service is beeing
|
||||||
|
# updated, but more services could be added.
|
||||||
|
#
|
||||||
|
# For instance:
|
||||||
|
# ```sh
|
||||||
|
# export ROUTER_OS_ADDITIONAL_SERVICES="/ip service set api-ssl certificate=$_cdomain.cer_0"
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# One optional thing to do as well is to create a script that updates
|
||||||
|
# all the required services and run that script in a single command.
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
routeros_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_HOST
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_HOST" ]; then
|
||||||
|
_debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct."
|
||||||
|
ROUTER_OS_HOST="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_USERNAME
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_USERNAME" ]; then
|
||||||
|
_err "Need to set the env variable ROUTER_OS_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_ADDITIONAL_SERVICES
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then
|
||||||
|
_debug "Not enabling additional services"
|
||||||
|
ROUTER_OS_ADDITIONAL_SERVICES=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf ROUTER_OS_HOST "$ROUTER_OS_HOST"
|
||||||
|
_savedeployconf ROUTER_OS_USERNAME "$ROUTER_OS_USERNAME"
|
||||||
|
_savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES"
|
||||||
|
|
||||||
|
_info "Trying to push key '$_ckey' to router"
|
||||||
|
scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"
|
||||||
|
_info "Trying to push cert '$_cfullchain' to router"
|
||||||
|
scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"
|
||||||
|
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive \
|
||||||
|
source=\"## generated by routeros deploy script in acme.sh;\
|
||||||
|
\n/certificate remove [ find name=$_cdomain.cer_0 ];\
|
||||||
|
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
|
||||||
|
\ndelay 1;\
|
||||||
|
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
|
||||||
|
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
|
||||||
|
\ndelay 1;\
|
||||||
|
\n/file remove $_cdomain.cer;\
|
||||||
|
\n/file remove $_cdomain.key;\
|
||||||
|
\ndelay 2;\
|
||||||
|
\n/ip service set www-ssl certificate=$_cdomain.cer_0;\
|
||||||
|
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
|
||||||
|
\n\"
|
||||||
|
"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$DEPLOY_SCRIPT_CMD"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script run \"LE Cert Deploy - $_cdomain\""
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script remove \"LE Cert Deploy - $_cdomain\""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
303
deploy/ssh.sh
Normal file
303
deploy/ssh.sh
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificates to remote server by SSH
|
||||||
|
# Note that SSH must be able to login to remote host without a password...
|
||||||
|
# SSH Keys must have been exchanged with the remote host. Validate and
|
||||||
|
# test that you can login to USER@SERVER from the host running acme.sh before
|
||||||
|
# using this script.
|
||||||
|
#
|
||||||
|
# The following variables exported from environment will be used.
|
||||||
|
# If not set then values previously saved in domain.conf file are used.
|
||||||
|
#
|
||||||
|
# Only a username is required. All others are optional.
|
||||||
|
#
|
||||||
|
# The following examples are for QNAP NAS running QTS 4.2
|
||||||
|
# export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
|
||||||
|
# export DEPLOY_SSH_USER="admin" # required
|
||||||
|
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name
|
||||||
|
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
|
||||||
|
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
|
||||||
|
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
|
||||||
|
# export DEPLOY_SSH_FULLCHAIN=""
|
||||||
|
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
|
||||||
|
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
|
||||||
|
# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
|
||||||
|
# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
ssh_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_deploy_ssh_servers=""
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# USER is required to login by SSH to remote host.
|
||||||
|
_getdeployconf DEPLOY_SSH_USER
|
||||||
|
_debug2 DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
|
||||||
|
if [ -z "$DEPLOY_SSH_USER" ]; then
|
||||||
|
if [ -z "$Le_Deploy_ssh_user" ]; then
|
||||||
|
_err "DEPLOY_SSH_USER not defined."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Le_Deploy_ssh_user="$DEPLOY_SSH_USER"
|
||||||
|
_savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SERVER is optional. If not provided then use _cdomain
|
||||||
|
_getdeployconf DEPLOY_SSH_SERVER
|
||||||
|
_debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
|
||||||
|
if [ -n "$DEPLOY_SSH_SERVER" ]; then
|
||||||
|
Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER"
|
||||||
|
_savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server"
|
||||||
|
elif [ -z "$Le_Deploy_ssh_server" ]; then
|
||||||
|
Le_Deploy_ssh_server="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CMD is optional. If not provided then use ssh
|
||||||
|
_getdeployconf DEPLOY_SSH_CMD
|
||||||
|
_debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
|
||||||
|
if [ -n "$DEPLOY_SSH_CMD" ]; then
|
||||||
|
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
|
||||||
|
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
|
||||||
|
elif [ -z "$Le_Deploy_ssh_cmd" ]; then
|
||||||
|
Le_Deploy_ssh_cmd="ssh -T"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BACKUP is optional. If not provided then default to previously saved value or yes.
|
||||||
|
_getdeployconf DEPLOY_SSH_BACKUP
|
||||||
|
_debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
|
||||||
|
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
|
||||||
|
Le_Deploy_ssh_backup="no"
|
||||||
|
elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
Le_Deploy_ssh_backup="yes"
|
||||||
|
fi
|
||||||
|
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
|
||||||
|
|
||||||
|
# BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
|
||||||
|
_getdeployconf DEPLOY_SSH_BACKUP_PATH
|
||||||
|
_debug2 DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
|
||||||
|
if [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then
|
||||||
|
Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH"
|
||||||
|
elif [ -z "$Le_Deploy_ssh_backup_path" ]; then
|
||||||
|
Le_Deploy_ssh_backup_path=".acme_ssh_deploy"
|
||||||
|
fi
|
||||||
|
_savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path"
|
||||||
|
|
||||||
|
# MULTI_CALL is optional. If not provided then default to previously saved
|
||||||
|
# value (which may be undefined... equivalent to "no").
|
||||||
|
_getdeployconf DEPLOY_SSH_MULTI_CALL
|
||||||
|
_debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
Le_Deploy_ssh_multi_call="yes"
|
||||||
|
_savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call"
|
||||||
|
elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then
|
||||||
|
Le_Deploy_ssh_multi_call=""
|
||||||
|
_cleardomainconf Le_Deploy_ssh_multi_call
|
||||||
|
fi
|
||||||
|
|
||||||
|
_deploy_ssh_servers=$Le_Deploy_ssh_server
|
||||||
|
for Le_Deploy_ssh_server in $_deploy_ssh_servers; do
|
||||||
|
_ssh_deploy
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_ssh_deploy() {
|
||||||
|
_err_code=0
|
||||||
|
_cmdstr=""
|
||||||
|
_backupprefix=""
|
||||||
|
_backupdir=""
|
||||||
|
|
||||||
|
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
_info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
|
||||||
|
else
|
||||||
|
_info "Required commands batched and sent in single call to remote host"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
||||||
|
_backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup"
|
||||||
|
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
|
||||||
|
# run cleanup on the backup directory, erase all older
|
||||||
|
# than 180 days (15552000 seconds).
|
||||||
|
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
|
||||||
|
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
|
||||||
|
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
|
||||||
|
# Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
|
||||||
|
# Create our backup directory for overwritten cert files.
|
||||||
|
_cmdstr="mkdir -p $_backupdir; $_cmdstr"
|
||||||
|
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
|
||||||
|
_info "Backup directories erased after 180 days."
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# KEYFILE is optional.
|
||||||
|
# If provided then private key will be copied to provided filename.
|
||||||
|
_getdeployconf DEPLOY_SSH_KEYFILE
|
||||||
|
_debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
|
||||||
|
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
|
Le_Deploy_ssh_keyfile="$DEPLOY_SSH_KEYFILE"
|
||||||
|
_savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile"
|
||||||
|
fi
|
||||||
|
if [ -n "$Le_Deploy_ssh_keyfile" ]; then
|
||||||
|
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;"
|
||||||
|
fi
|
||||||
|
# copy new certificate into file.
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
|
||||||
|
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CERTFILE is optional.
|
||||||
|
# If provided then certificate will be copied or appended to provided filename.
|
||||||
|
_getdeployconf DEPLOY_SSH_CERTFILE
|
||||||
|
_debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
|
||||||
|
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
|
Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE"
|
||||||
|
_savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile"
|
||||||
|
fi
|
||||||
|
if [ -n "$Le_Deploy_ssh_certfile" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;"
|
||||||
|
fi
|
||||||
|
# copy new certificate into file.
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
|
||||||
|
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CAFILE is optional.
|
||||||
|
# If provided then CA intermediate certificate will be copied or appended to provided filename.
|
||||||
|
_getdeployconf DEPLOY_SSH_CAFILE
|
||||||
|
_debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
|
||||||
|
if [ -n "$DEPLOY_SSH_CAFILE" ]; then
|
||||||
|
Le_Deploy_ssh_cafile="$DEPLOY_SSH_CAFILE"
|
||||||
|
_savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile"
|
||||||
|
fi
|
||||||
|
if [ -n "$Le_Deploy_ssh_cafile" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] ||
|
||||||
|
[ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;"
|
||||||
|
fi
|
||||||
|
# copy new certificate into file.
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
|
||||||
|
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# FULLCHAIN is optional.
|
||||||
|
# If provided then fullchain certificate will be copied or appended to provided filename.
|
||||||
|
_getdeployconf DEPLOY_SSH_FULLCHAIN
|
||||||
|
_debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
|
||||||
|
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
|
||||||
|
Le_Deploy_ssh_fullchain="$DEPLOY_SSH_FULLCHAIN"
|
||||||
|
_savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain"
|
||||||
|
fi
|
||||||
|
if [ -n "$Le_Deploy_ssh_fullchain" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_keyfile" ] ||
|
||||||
|
[ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_certfile" ] ||
|
||||||
|
[ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_cafile" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;"
|
||||||
|
fi
|
||||||
|
# copy new certificate into file.
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
|
||||||
|
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# REMOTE_CMD is optional.
|
||||||
|
# If provided then this command will be executed on remote host.
|
||||||
|
_getdeployconf DEPLOY_SSH_REMOTE_CMD
|
||||||
|
_debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
|
||||||
|
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
|
||||||
|
Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD"
|
||||||
|
_savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd"
|
||||||
|
fi
|
||||||
|
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then
|
||||||
|
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;"
|
||||||
|
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd"
|
||||||
|
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if commands not all sent in multiple calls then all commands sent in a single SSH call now...
|
||||||
|
if [ -n "$_cmdstr" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#cmd
|
||||||
|
_ssh_remote_cmd() {
|
||||||
|
_cmd="$1"
|
||||||
|
_secure_debug "Remote commands to execute: $_cmd"
|
||||||
|
_info "Submitting sequence of commands to remote server by ssh"
|
||||||
|
# quotations in bash cmd below intended. Squash travis spellcheck error
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
$Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'"
|
||||||
|
_err_code="$?"
|
||||||
|
|
||||||
|
if [ "$_err_code" != "0" ]; then
|
||||||
|
_err "Error code $_err_code returned from ssh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $_err_code
|
||||||
|
}
|
||||||
55
deploy/strongswan.sh
Normal file
55
deploy/strongswan.sh
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a sample custom api script.
|
||||||
|
#This file name is "myapi.sh"
|
||||||
|
#So, here must be a method myapi_deploy()
|
||||||
|
#Which will be called by acme.sh to deploy the cert
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
strongswan_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_info "Using strongswan"
|
||||||
|
|
||||||
|
if [ -x /usr/sbin/ipsec ]; then
|
||||||
|
_ipsec=/usr/sbin/ipsec
|
||||||
|
elif [ -x /usr/sbin/strongswan ]; then
|
||||||
|
_ipsec=/usr/sbin/strongswan
|
||||||
|
elif [ -x /usr/local/sbin/ipsec ]; then
|
||||||
|
_ipsec=/usr/local/sbin/ipsec
|
||||||
|
else
|
||||||
|
_err "no strongswan or ipsec command is detected"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info _ipsec "$_ipsec"
|
||||||
|
|
||||||
|
_confdir=$($_ipsec --confdir)
|
||||||
|
if [ $? -ne 0 ] || [ -z "$_confdir" ]; then
|
||||||
|
_err "no strongswan --confdir is detected"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info _confdir "$_confdir"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")"
|
||||||
|
cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")"
|
||||||
|
cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")"
|
||||||
|
cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")"
|
||||||
|
|
||||||
|
$_ipsec reload
|
||||||
|
|
||||||
|
}
|
||||||
178
deploy/synology_dsm.sh
Normal file
178
deploy/synology_dsm.sh
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to Synology DSM
|
||||||
|
#
|
||||||
|
# It requires following environment variables:
|
||||||
|
#
|
||||||
|
# SYNO_Username - Synology Username to login (must be an administrator)
|
||||||
|
# SYNO_Password - Synology Password to login
|
||||||
|
# SYNO_Certificate - Certificate description to target for replacement
|
||||||
|
#
|
||||||
|
# The following environmental variables may be set if you don't like their
|
||||||
|
# default values:
|
||||||
|
#
|
||||||
|
# SYNO_Scheme - defaults to http
|
||||||
|
# SYNO_Hostname - defaults to localhost
|
||||||
|
# SYNO_Port - defaults to 5000
|
||||||
|
# SYNO_DID - device ID to skip OTP - defaults to empty
|
||||||
|
# SYNO_TOTP_SECRET - TOTP secret to generate OTP - defaults to empty
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# -------------
|
||||||
|
# - jq and curl
|
||||||
|
# - oathtool (When using 2 Factor Authentication and SYNO_TOTP_SECRET is set)
|
||||||
|
#
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
synology_dsm_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
|
||||||
|
# Get Username and Password, but don't save until we successfully authenticate
|
||||||
|
_getdeployconf SYNO_Username
|
||||||
|
_getdeployconf SYNO_Password
|
||||||
|
_getdeployconf SYNO_Create
|
||||||
|
_getdeployconf SYNO_DID
|
||||||
|
_getdeployconf SYNO_TOTP_SECRET
|
||||||
|
if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
|
||||||
|
_err "SYNO_Username & SYNO_Password must be set"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 SYNO_Username "$SYNO_Username"
|
||||||
|
_secure_debug2 SYNO_Password "$SYNO_Password"
|
||||||
|
|
||||||
|
# Optional scheme, hostname, and port for Synology DSM
|
||||||
|
_getdeployconf SYNO_Scheme
|
||||||
|
_getdeployconf SYNO_Hostname
|
||||||
|
_getdeployconf SYNO_Port
|
||||||
|
|
||||||
|
# default vaules for scheme, hostname, and port
|
||||||
|
# defaulting to localhost and http because it's localhost...
|
||||||
|
[ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http"
|
||||||
|
[ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost"
|
||||||
|
[ -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"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
if printf "%s" "$SYNO_Certificate" | grep '\\'; then
|
||||||
|
_err "Do not use a backslash (\) in your certificate description"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
|
||||||
|
_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")
|
||||||
|
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
|
||||||
|
_debug3 response "$response"
|
||||||
|
_debug3 api_version "$api_version"
|
||||||
|
|
||||||
|
# Login, get the token from JSON and session id from cookie
|
||||||
|
_info "Logging into $SYNO_Hostname:$SYNO_Port"
|
||||||
|
encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
|
||||||
|
encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
|
||||||
|
|
||||||
|
otp_code=""
|
||||||
|
if [ -n "$SYNO_TOTP_SECRET" ]; then
|
||||||
|
otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SYNO_DID" ]; then
|
||||||
|
_H1="Cookie: did=$SYNO_DID"
|
||||||
|
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=$otp_code" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
|
||||||
|
token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
|
||||||
|
_debug3 response "$response"
|
||||||
|
_debug token "$token"
|
||||||
|
|
||||||
|
if [ -z "$token" ]; then
|
||||||
|
_err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
|
||||||
|
_err "Check your username and password."
|
||||||
|
_err "If two-factor authentication is enabled for the user, set SYNO_TOTP_SECRET."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
|
||||||
|
|
||||||
|
_H1="X-SYNO-TOKEN: $token"
|
||||||
|
export _H1
|
||||||
|
_debug2 H1 "${_H1}"
|
||||||
|
|
||||||
|
# Now that we know the username and password are good, save them
|
||||||
|
_savedeployconf SYNO_Username "$SYNO_Username"
|
||||||
|
_savedeployconf SYNO_Password "$SYNO_Password"
|
||||||
|
_savedeployconf SYNO_DID "$SYNO_DID"
|
||||||
|
_savedeployconf SYNO_TOTP_SECRET "$SYNO_TOTP_SECRET"
|
||||||
|
|
||||||
|
_info "Getting certificates in Synology DSM"
|
||||||
|
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
|
||||||
|
_debug3 response "$response"
|
||||||
|
escaped_certificate="$(printf "%s" "$SYNO_Certificate" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
|
||||||
|
_debug escaped_certificate "$escaped_certificate"
|
||||||
|
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
|
||||||
|
_debug2 id "$id"
|
||||||
|
|
||||||
|
if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
|
||||||
|
_err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# we've verified this certificate description is a thing, so save it
|
||||||
|
_savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64"
|
||||||
|
|
||||||
|
_info "Generate form POST request"
|
||||||
|
nl="\0015\0012"
|
||||||
|
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="$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=\"id\"${nl}${nl}$id"
|
||||||
|
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
|
||||||
|
_debug2 default "this is the default certificate"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
|
||||||
|
else
|
||||||
|
_debug2 default "this is NOT the default certificate"
|
||||||
|
fi
|
||||||
|
content="$content${nl}--$delim--${nl}"
|
||||||
|
content="$(printf "%b_" "$content")"
|
||||||
|
content="${content%_}" # protect trailing \n
|
||||||
|
|
||||||
|
_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}")
|
||||||
|
_debug3 response "$response"
|
||||||
|
|
||||||
|
if ! echo "$response" | grep '"error":' >/dev/null; then
|
||||||
|
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
|
||||||
|
_info "http services were restarted"
|
||||||
|
else
|
||||||
|
_info "http services were NOT restarted"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Unable to update certificate, error code $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
180
deploy/truenas.sh
Normal file
180
deploy/truenas.sh
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a scipt to deploy the cert to your TrueNAS using the REST API.
|
||||||
|
# https://www.truenas.com/docs/hub/additional-topics/api/rest_api.html
|
||||||
|
#
|
||||||
|
# Written by Frank Plass github@f-plass.de
|
||||||
|
# https://github.com/danb35/deploy-freenas/blob/master/deploy_freenas.py
|
||||||
|
# Thanks to danb35 for your template!
|
||||||
|
#
|
||||||
|
# Following environment variables must be set:
|
||||||
|
#
|
||||||
|
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
|
||||||
|
#
|
||||||
|
# The following environmental variables may be set if you don't like their
|
||||||
|
# default values:
|
||||||
|
#
|
||||||
|
# DEPLOY_TRUENAS_HOSTNAME - defaults to localhost
|
||||||
|
# DEPLOY_TRUENAS_SCHEME - defaults to http, set alternatively to https
|
||||||
|
#
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
truenas_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_APIKEY
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
|
||||||
|
_err "TrueNAS Api Key is not found, please define DEPLOY_TRUENAS_APIKEY."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
|
||||||
|
# Optional hostname, scheme for TrueNAS
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_HOSTNAME
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_SCHEME
|
||||||
|
|
||||||
|
# default values for hostname and scheme
|
||||||
|
[ -n "${DEPLOY_TRUENAS_HOSTNAME}" ] || DEPLOY_TRUENAS_HOSTNAME="localhost"
|
||||||
|
[ -n "${DEPLOY_TRUENAS_SCHEME}" ] || DEPLOY_TRUENAS_SCHEME="http"
|
||||||
|
|
||||||
|
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_debug2 DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
|
||||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
|
||||||
|
_debug _api_url "$_api_url"
|
||||||
|
|
||||||
|
_H1="Authorization: Bearer $DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_secure_debug3 _H1 "$_H1"
|
||||||
|
|
||||||
|
_info "Testing Connection TrueNAS"
|
||||||
|
_response=$(_get "$_api_url/system/state")
|
||||||
|
_info "TrueNAS System State: $_response."
|
||||||
|
|
||||||
|
if [ -z "$_response" ]; then
|
||||||
|
_err "Unable to authenticate to $_api_url."
|
||||||
|
_err 'Check your Connection and set DEPLOY_TRUENAS_HOSTNAME="192.168.178.x".'
|
||||||
|
_err 'or'
|
||||||
|
_err 'set DEPLOY_TRUENAS_HOSTNAME="<truenas_dnsname>".'
|
||||||
|
_err 'Check your Connection and set DEPLOY_TRUENAS_SCHEME="https".'
|
||||||
|
_err "Check your Api Key."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
|
||||||
|
_info "Getting active certificate from TrueNAS"
|
||||||
|
_response=$(_get "$_api_url/system/general")
|
||||||
|
_active_cert_id=$(echo "$_response" | grep -B2 '"name":' | grep 'id' | tr -d -- '"id: ,')
|
||||||
|
_active_cert_name=$(echo "$_response" | grep '"name":' | sed -n 's/.*: "\(.\{1,\}\)",$/\1/p')
|
||||||
|
_param_httpsredirect=$(echo "$_response" | grep '"ui_httpsredirect":' | sed -n 's/.*": \(.\{1,\}\),$/\1/p')
|
||||||
|
_debug Active_UI_Certificate_ID "$_active_cert_id"
|
||||||
|
_debug Active_UI_Certificate_Name "$_active_cert_name"
|
||||||
|
_debug Active_UI_http_redirect "$_param_httpsredirect"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_TRUENAS_SCHEME" = "http" ] && [ "$_param_httpsredirect" = "true" ]; then
|
||||||
|
_info "http Redirect active"
|
||||||
|
_info "Setting DEPLOY_TRUENAS_SCHEME to 'https'"
|
||||||
|
DEPLOY_TRUENAS_SCHEME="https"
|
||||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Upload new certifikate to TrueNAS"
|
||||||
|
_certname="Letsencrypt_$(_utc_date | tr ' ' '_' | tr -d -- ':')"
|
||||||
|
_debug3 _certname "$_certname"
|
||||||
|
|
||||||
|
_certData="{\"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"name\": \"${_certname}\", \"certificate\": \"$(_json_encode <"$_cfullchain")\", \"privatekey\": \"$(_json_encode <"$_ckey")\"}"
|
||||||
|
_add_cert_result="$(_post "$_certData" "$_api_url/certificate" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _add_cert_result "$_add_cert_result"
|
||||||
|
|
||||||
|
_info "Getting Certificate list to get new Cert ID"
|
||||||
|
_cert_list=$(_get "$_api_url/system/general/ui_certificate_choices")
|
||||||
|
_cert_id=$(echo "$_cert_list" | grep "$_certname" | sed -n 's/.*"\([0-9]\{1,\}\)".*$/\1/p')
|
||||||
|
|
||||||
|
_debug3 _cert_id "$_cert_id"
|
||||||
|
|
||||||
|
_info "Activate Certificate ID: $_cert_id"
|
||||||
|
_activateData="{\"ui_certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_result="$(_post "$_activateData" "$_api_url/system/general" "" "PUT" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _activate_result "$_activate_result"
|
||||||
|
|
||||||
|
_info "Check if WebDAV certificate is the same as the WEB UI"
|
||||||
|
_webdav_list=$(_get "$_api_url/webdav")
|
||||||
|
_webdav_cert_id=$(echo "$_webdav_list" | grep '"certssl":' | tr -d -- '"certsl: ,')
|
||||||
|
|
||||||
|
if [ "$_webdav_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Update the WebDAV Certificate"
|
||||||
|
_debug _webdav_cert_id "$_webdav_cert_id"
|
||||||
|
_webdav_data="{\"certssl\": \"${_cert_id}\"}"
|
||||||
|
_activate_webdav_cert="$(_post "$_webdav_data" "$_api_url/webdav" "" "PUT" "application/json")"
|
||||||
|
_webdav_new_cert_id=$(echo "$_activate_webdav_cert" | _json_decode | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p')
|
||||||
|
if [ "$_webdav_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "WebDAV Certificate update successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set WebDAV certificate"
|
||||||
|
_debug3 _activate_webdav_cert "$_activate_webdav_cert"
|
||||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
|
else
|
||||||
|
_info "WebDAV certificate not set or not the same as Web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Check if FTP certificate is the same as the WEB UI"
|
||||||
|
_ftp_list=$(_get "$_api_url/ftp")
|
||||||
|
_ftp_cert_id=$(echo "$_ftp_list" | grep '"ssltls_certificate":' | tr -d -- '"certislfa:_ ,')
|
||||||
|
|
||||||
|
if [ "$_ftp_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Update the FTP Certificate"
|
||||||
|
_debug _ftp_cert_id "$_ftp_cert_id"
|
||||||
|
_ftp_data="{\"ssltls_certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_ftp_cert="$(_post "$_ftp_data" "$_api_url/ftp" "" "PUT" "application/json")"
|
||||||
|
_ftp_new_cert_id=$(echo "$_activate_ftp_cert" | _json_decode | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p')
|
||||||
|
if [ "$_ftp_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "FTP Certificate update successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set FTP certificate"
|
||||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert"
|
||||||
|
_debug3 _ftp_new_cert_id "$_ftp_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert"
|
||||||
|
else
|
||||||
|
_info "FTP certificate not set or not the same as Web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Delete old Certificate"
|
||||||
|
_delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _delete_result "$_delete_result"
|
||||||
|
|
||||||
|
_info "Reload WebUI from TrueNAS"
|
||||||
|
_restart_UI=$(_get "$_api_url/system/general/ui_restart")
|
||||||
|
_debug2 _restart_UI "$_restart_UI"
|
||||||
|
|
||||||
|
if [ -n "$_add_cert_result" ] && [ -n "$_activate_result" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Certupdate was not succesfull, please use --debug"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
214
deploy/unifi.sh
Normal file
214
deploy/unifi.sh
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert on a Unifi Controller or Cloud Key device.
|
||||||
|
# It supports:
|
||||||
|
# - self-hosted Unifi Controller
|
||||||
|
# - Unifi Cloud Key (Gen1/2/2+)
|
||||||
|
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
|
||||||
|
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
# The deploy-hook automatically detects standard Unifi installations
|
||||||
|
# for each of the supported environments. Most users should not need
|
||||||
|
# to set any of these variables, but if you are running a self-hosted
|
||||||
|
# Controller with custom locations, set these as necessary before running
|
||||||
|
# the deploy hook. (Defaults shown below.)
|
||||||
|
#
|
||||||
|
# Settings for Unifi Controller:
|
||||||
|
# Location of Java keystore or unifi.keystore.jks file:
|
||||||
|
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
|
||||||
|
# Keystore password (built into Unifi Controller, not a user-set password):
|
||||||
|
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
||||||
|
# Command to restart Unifi Controller:
|
||||||
|
#DEPLOY_UNIFI_RELOAD="service unifi restart"
|
||||||
|
#
|
||||||
|
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
|
||||||
|
# Directory where cloudkey.crt and cloudkey.key live:
|
||||||
|
#DEPLOY_UNIFI_CLOUDKEY_CERTDIR="/etc/ssl/private"
|
||||||
|
# Command to restart maintenance pages and Controller
|
||||||
|
# (same setting as above, default is updated when running on Cloud Key Gen1):
|
||||||
|
#DEPLOY_UNIFI_RELOAD="service nginx restart && service unifi restart"
|
||||||
|
#
|
||||||
|
# Settings for UnifiOS (Cloud Key Gen2):
|
||||||
|
# Directory where unifi-core.crt and unifi-core.key live:
|
||||||
|
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
|
||||||
|
# Command to restart unifi-core:
|
||||||
|
#DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core"
|
||||||
|
#
|
||||||
|
# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR,
|
||||||
|
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
unifi_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_UNIFI_KEYSTORE
|
||||||
|
_getdeployconf DEPLOY_UNIFI_KEYPASS
|
||||||
|
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
|
||||||
|
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
|
||||||
|
_getdeployconf DEPLOY_UNIFI_RELOAD
|
||||||
|
|
||||||
|
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
||||||
|
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
||||||
|
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
|
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
|
_debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
||||||
|
|
||||||
|
# Space-separated list of environments detected and installed:
|
||||||
|
_services_updated=""
|
||||||
|
|
||||||
|
# Default reload commands accumulated as we auto-detect environments:
|
||||||
|
_reload_cmd=""
|
||||||
|
|
||||||
|
# Unifi Controller environment (self hosted or any Cloud Key) --
|
||||||
|
# auto-detect by file /usr/lib/unifi/data/keystore:
|
||||||
|
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
|
||||||
|
if [ -f "$_unifi_keystore" ]; then
|
||||||
|
_info "Installing certificate for Unifi Controller (Java keystore)"
|
||||||
|
_debug _unifi_keystore "$_unifi_keystore"
|
||||||
|
if ! _exists keytool; then
|
||||||
|
_err "keytool not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ ! -w "$_unifi_keystore" ]; then
|
||||||
|
_err "The file $_unifi_keystore is not writable, please change the permission."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-aircontrolenterprise}"
|
||||||
|
|
||||||
|
_debug "Generate import pkcs12"
|
||||||
|
_import_pkcs12="$(_mktemp)"
|
||||||
|
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error generating pkcs12. Please re-run with --debug and report a bug."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Import into keystore: $_unifi_keystore"
|
||||||
|
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!"
|
||||||
|
rm "$_import_pkcs12"
|
||||||
|
else
|
||||||
|
_err "Error importing into Unifi Java keystore."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
rm "$_import_pkcs12"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl -q is-active unifi; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart"
|
||||||
|
fi
|
||||||
|
_services_updated="${_services_updated} unifi"
|
||||||
|
_info "Install Unifi Controller certificate success!"
|
||||||
|
elif [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_KEYSTORE='$DEPLOY_UNIFI_KEYSTORE' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cloud Key environment (non-UnifiOS -- nginx serves admin pages) --
|
||||||
|
# auto-detect by file /etc/ssl/private/cloudkey.key:
|
||||||
|
_cloudkey_certdir="${DEPLOY_UNIFI_CLOUDKEY_CERTDIR:-/etc/ssl/private}"
|
||||||
|
if [ -f "${_cloudkey_certdir}/cloudkey.key" ]; then
|
||||||
|
_info "Installing certificate for Cloud Key Gen1 (nginx admin pages)"
|
||||||
|
_debug _cloudkey_certdir "$_cloudkey_certdir"
|
||||||
|
if [ ! -w "$_cloudkey_certdir" ]; then
|
||||||
|
_err "The directory $_cloudkey_certdir is not writable; please check permissions."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
|
||||||
|
# Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was
|
||||||
|
# updated above), but if not, we don't know how to handle this installation:
|
||||||
|
if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then
|
||||||
|
_err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
|
||||||
|
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
|
||||||
|
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
|
||||||
|
|
||||||
|
if systemctl -q is-active nginx; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service nginx restart"
|
||||||
|
fi
|
||||||
|
_info "Install Cloud Key Gen1 certificate success!"
|
||||||
|
_services_updated="${_services_updated} nginx"
|
||||||
|
elif [ "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_CLOUDKEY_CERTDIR='$DEPLOY_UNIFI_CLOUDKEY_CERTDIR' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# UnifiOS environment -- auto-detect by /data/unifi-core/config/unifi-core.key:
|
||||||
|
_unifi_core_config="${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}"
|
||||||
|
if [ -f "${_unifi_core_config}/unifi-core.key" ]; then
|
||||||
|
_info "Installing certificate for UnifiOS"
|
||||||
|
_debug _unifi_core_config "$_unifi_core_config"
|
||||||
|
if [ ! -w "$_unifi_core_config" ]; then
|
||||||
|
_err "The directory $_unifi_core_config is not writable; please check permissions."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
|
||||||
|
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
|
||||||
|
|
||||||
|
if systemctl -q is-active unifi-core; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core"
|
||||||
|
fi
|
||||||
|
_info "Install UnifiOS certificate success!"
|
||||||
|
_services_updated="${_services_updated} unifi-core"
|
||||||
|
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_CORE_CONFIG='$DEPLOY_UNIFI_CORE_CONFIG' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_services_updated" ]; then
|
||||||
|
# None of the Unifi environments were auto-detected, so no deployment has occurred
|
||||||
|
# (and none of DEPLOY_UNIFI_{KEYSTORE,CLOUDKEY_CERTDIR,CORE_CONFIG} were set).
|
||||||
|
_err "Unable to detect Unifi environment in standard location."
|
||||||
|
_err "(This deploy hook must be run on the Unifi device, not a remote machine.)"
|
||||||
|
_err "For non-standard Unifi installations, set DEPLOY_UNIFI_KEYSTORE,"
|
||||||
|
_err "DEPLOY_UNIFI_CLOUDKEY_CERTDIR, and/or DEPLOY_UNIFI_CORE_CONFIG as appropriate."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_reload_cmd="${DEPLOY_UNIFI_RELOAD:-$_reload_cmd}"
|
||||||
|
if [ -z "$_reload_cmd" ]; then
|
||||||
|
_err "Certificates were installed for services:${_services_updated},"
|
||||||
|
_err "but none appear to be active. Please set DEPLOY_UNIFI_RELOAD"
|
||||||
|
_err "to a command that will restart the necessary services."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Reload services (this may take some time): $_reload_cmd"
|
||||||
|
if eval "$_reload_cmd"; then
|
||||||
|
_info "Reload success!"
|
||||||
|
else
|
||||||
|
_err "Reload error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Successful, so save all (non-default) config:
|
||||||
|
_savedeployconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
78
deploy/vault.sh
Normal file
78
deploy/vault.sh
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to hashicorp vault using curl
|
||||||
|
# (https://www.vaultproject.io/)
|
||||||
|
#
|
||||||
|
# it requires following environment variables:
|
||||||
|
#
|
||||||
|
# VAULT_PREFIX - this contains the prefix path in vault
|
||||||
|
# VAULT_ADDR - vault requires this to find your vault server
|
||||||
|
#
|
||||||
|
# additionally, you need to ensure that VAULT_TOKEN is avialable
|
||||||
|
# to access the vault server
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
vault_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# validate required env vars
|
||||||
|
_getdeployconf VAULT_PREFIX
|
||||||
|
if [ -z "$VAULT_PREFIX" ]; then
|
||||||
|
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_ADDR
|
||||||
|
if [ -z "$VAULT_ADDR" ]; then
|
||||||
|
_err "VAULT_ADDR needs to be defined (contains vault connection address)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf VAULT_ADDR "$VAULT_ADDR"
|
||||||
|
|
||||||
|
# JSON does not allow multiline strings.
|
||||||
|
# So replacing new-lines with "\n" here
|
||||||
|
_ckey=$(sed -z 's/\n/\\n/g' <"$2")
|
||||||
|
_ccert=$(sed -z 's/\n/\\n/g' <"$3")
|
||||||
|
_cca=$(sed -z 's/\n/\\n/g' <"$4")
|
||||||
|
_cfullchain=$(sed -z 's/\n/\\n/g' <"$5")
|
||||||
|
|
||||||
|
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
|
||||||
|
export _H1="X-Vault-Token: $VAULT_TOKEN"
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
|
_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL"
|
||||||
|
else
|
||||||
|
_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
|
_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem"
|
||||||
|
_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key"
|
||||||
|
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem"
|
||||||
|
_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem"
|
||||||
|
else
|
||||||
|
_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem"
|
||||||
|
_post "{\"value\": \"$_ckey\"}" "$URL/cert.key"
|
||||||
|
_post "{\"value\": \"$_cca\"}" "$URL/chain.pem"
|
||||||
|
_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
61
deploy/vault_cli.sh
Normal file
61
deploy/vault_cli.sh
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to hashicorp vault
|
||||||
|
# (https://www.vaultproject.io/)
|
||||||
|
#
|
||||||
|
# it requires the vault binary to be available in PATH, and the following
|
||||||
|
# environment variables:
|
||||||
|
#
|
||||||
|
# VAULT_PREFIX - this contains the prefix path in vault
|
||||||
|
# VAULT_ADDR - vault requires this to find your vault server
|
||||||
|
#
|
||||||
|
# additionally, you need to ensure that VAULT_TOKEN is avialable or
|
||||||
|
# `vault auth` has applied the appropriate authorization for the vault binary
|
||||||
|
# to access the vault server
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
vault_cli_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# validate required env vars
|
||||||
|
if [ -z "$VAULT_PREFIX" ]; then
|
||||||
|
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$VAULT_ADDR" ]; then
|
||||||
|
_err "VAULT_ADDR needs to be defined (contains vault connection address)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VAULT_CMD=$(command -v vault)
|
||||||
|
if [ ! $? ]; then
|
||||||
|
_err "cannot find vault binary!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
|
||||||
|
else
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
110
deploy/vsftpd.sh
Normal file
110
deploy/vsftpd.sh
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to vsftpd server.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
|
||||||
|
#DEPLOY_VSFTPD_RELOAD="service vsftpd restart"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
vsftpd_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_ssl_path="/etc/acme.sh/vsftpd"
|
||||||
|
if ! mkdir -p "$_ssl_path"; then
|
||||||
|
_err "Can not create folder:$_ssl_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Copying key and cert"
|
||||||
|
_real_key="$_ssl_path/vsftpd.key"
|
||||||
|
if ! cat "$_ckey" >"$_real_key"; then
|
||||||
|
_err "Error: write key file to: $_real_key"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_real_fullchain="$_ssl_path/vsftpd.chain.pem"
|
||||||
|
if ! cat "$_cfullchain" >"$_real_fullchain"; then
|
||||||
|
_err "Error: write key file to: $_real_fullchain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEFAULT_VSFTPD_RELOAD="service vsftpd restart"
|
||||||
|
_reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}"
|
||||||
|
|
||||||
|
if [ -z "$IS_RENEW" ]; then
|
||||||
|
DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf"
|
||||||
|
_vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}"
|
||||||
|
if [ ! -f "$_vsftpd_conf" ]; then
|
||||||
|
if [ -z "$DEPLOY_VSFTPD_CONF" ]; then
|
||||||
|
_err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_err "It seems that the specified vsftpd conf is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ ! -w "$_vsftpd_conf" ]; then
|
||||||
|
_err "The file $_vsftpd_conf is not writable, please change the permission."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak"
|
||||||
|
_info "Backup $_vsftpd_conf to $_backup_conf"
|
||||||
|
cp "$_vsftpd_conf" "$_backup_conf"
|
||||||
|
|
||||||
|
_info "Modify vsftpd conf: $_vsftpd_conf"
|
||||||
|
if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" &&
|
||||||
|
_setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" &&
|
||||||
|
_setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
|
||||||
|
_info "Set config success!"
|
||||||
|
else
|
||||||
|
_err "Config vsftpd server error, please report bug to us."
|
||||||
|
_info "Restoring vsftpd conf"
|
||||||
|
if cat "$_backup_conf" >"$_vsftpd_conf"; then
|
||||||
|
_info "Restore conf success"
|
||||||
|
eval "$_reload"
|
||||||
|
else
|
||||||
|
_err "Oops, error restore vsftpd conf, please report bug to us."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Run reload: $_reload"
|
||||||
|
if eval "$_reload"; then
|
||||||
|
_info "Reload success!"
|
||||||
|
if [ "$DEPLOY_VSFTPD_CONF" ]; then
|
||||||
|
_savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF"
|
||||||
|
else
|
||||||
|
_cleardomainconf DEPLOY_VSFTPD_CONF
|
||||||
|
fi
|
||||||
|
if [ "$DEPLOY_VSFTPD_RELOAD" ]; then
|
||||||
|
_savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD"
|
||||||
|
else
|
||||||
|
_cleardomainconf DEPLOY_VSFTPD_RELOAD
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Reload error, restoring"
|
||||||
|
if cat "$_backup_conf" >"$_vsftpd_conf"; then
|
||||||
|
_info "Restore conf success"
|
||||||
|
eval "$_reload"
|
||||||
|
else
|
||||||
|
_err "Oops, error restore vsftpd conf, please report bug to us."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -1,90 +1,6 @@
|
|||||||
# How to use dns api
|
# How to use DNS API
|
||||||
|
DNS api usage:
|
||||||
## Use CloudFlare domain api to automatically issue cert
|
|
||||||
|
|
||||||
For now, we support clourflare integeration.
|
|
||||||
|
|
||||||
First you need to login to your clourflare account to get your api key.
|
|
||||||
|
|
||||||
```
|
|
||||||
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
|
||||||
|
|
||||||
export CF_Email="xxxx@sss.com"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Ok, let's issue cert now:
|
|
||||||
```
|
|
||||||
acme.sh --issue --dns dns_cf -d aa.com -d www.aa.com
|
|
||||||
```
|
|
||||||
|
|
||||||
The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key.
|
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||||
## Use Dnspod.cn domain api to automatically issue cert
|
|
||||||
|
|
||||||
For now, we support dnspod.cn integeration.
|
|
||||||
|
|
||||||
First you need to login to your dnspod.cn account to get your api key and key id.
|
|
||||||
|
|
||||||
```
|
|
||||||
export DP_Id="1234"
|
|
||||||
|
|
||||||
export DP_Key="sADDsdasdgdsf"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Ok, let's issue cert now:
|
|
||||||
```
|
|
||||||
acme.sh --issue --dns dns_dp -d aa.com -d www.aa.com
|
|
||||||
```
|
|
||||||
|
|
||||||
The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf`, when next time you use dnspod.cn api, it will reuse this key.
|
|
||||||
|
|
||||||
|
|
||||||
## Use Cloudxns.com domain api to automatically issue cert
|
|
||||||
|
|
||||||
For now, we support Cloudxns.com integeration.
|
|
||||||
|
|
||||||
First you need to login to your Cloudxns.com account to get your api key and key secret.
|
|
||||||
|
|
||||||
```
|
|
||||||
export CX_Key="1234"
|
|
||||||
|
|
||||||
export CX_Secret="sADDsdasdgdsf"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Ok, let's issue cert now:
|
|
||||||
```
|
|
||||||
acme.sh --issue --dns dns_cx -d aa.com -d www.aa.com
|
|
||||||
```
|
|
||||||
|
|
||||||
The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf`, when next time you use Cloudxns.com api, it will reuse this key.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Use custom api
|
|
||||||
|
|
||||||
If your api is not supported yet, you can write your own dns api.
|
|
||||||
|
|
||||||
Let's assume you want to name it 'myapi',
|
|
||||||
|
|
||||||
1. Create a bash script named `~/.acme.sh/dns_myapi.sh`,
|
|
||||||
2. In the scrypt, you must have a function named `dns_myapi_add()`. Which will be called by acme.sh to add dns records.
|
|
||||||
3. Then you can use your api to issue cert like:
|
|
||||||
|
|
||||||
```
|
|
||||||
acme.sh --issue --dns dns_myapi -d aa.com -d www.aa.com
|
|
||||||
```
|
|
||||||
|
|
||||||
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Use lexicon dns api
|
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
261
dnsapi/dns_1984hosting.sh
Executable file
261
dnsapi/dns_1984hosting.sh
Executable file
@@ -0,0 +1,261 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#This file name is "dns_1984hosting.sh"
|
||||||
|
#So, here must be a method dns_1984hosting_add()
|
||||||
|
#Which will be called by acme.sh to add the txt record to your api system.
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#Author: Adrian Fedoreanu
|
||||||
|
#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
|
||||||
|
#
|
||||||
|
# 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() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Add TXT record using 1984Hosting"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _1984hosting_login; then
|
||||||
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain" "$fulldomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Add TXT record $fulldomain with value '$txtvalue'"
|
||||||
|
value="$(printf '%s' "$txtvalue" | _url_encode)"
|
||||||
|
url="https://management.1984hosting.com/domains/entry/"
|
||||||
|
|
||||||
|
postdata="entry=new"
|
||||||
|
postdata="$postdata&type=TXT"
|
||||||
|
postdata="$postdata&ttl=900"
|
||||||
|
postdata="$postdata&zone=$_domain"
|
||||||
|
postdata="$postdata&host=$_sub_domain"
|
||||||
|
postdata="$postdata&rdata=%22$value%22"
|
||||||
|
_debug2 postdata "$postdata"
|
||||||
|
|
||||||
|
_authpost "$postdata" "$url"
|
||||||
|
response="$(echo "$_response" | _normalizeJson)"
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if _contains "$response" '"haserrors": true'; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post"
|
||||||
|
return 1
|
||||||
|
elif _contains "$response" "html>"; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
elif _contains "$response" '"auth": false'; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_1984hosting_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Delete TXT record using 1984Hosting"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _1984hosting_login; then
|
||||||
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain" "$fulldomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug "Delete $fulldomain TXT record"
|
||||||
|
|
||||||
|
url="https://management.1984hosting.com/domains"
|
||||||
|
if ! _get_zone_id "$url" "$_domain"; then
|
||||||
|
_err "invalid zone" "$_domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_htmlget "$url/$_zone_id" "$txtvalue"
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
|
||||||
|
_debug2 entry_id "$entry_id"
|
||||||
|
if [ -z "$entry_id" ]; then
|
||||||
|
_err "Error getting TXT entry_id for $1"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authpost "entry=$entry_id" "$url/delentry/"
|
||||||
|
response="$(echo "$_response" | _normalizeJson)"
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if ! _contains "$response" '"ok": true'; then
|
||||||
|
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# usage: _1984hosting_login username password
|
||||||
|
# returns 0 success
|
||||||
|
_1984hosting_login() {
|
||||||
|
if ! _check_credentials; then return 1; fi
|
||||||
|
|
||||||
|
if _check_cookies; then
|
||||||
|
_debug "Already logged in"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Login to 1984Hosting as user $One984HOSTING_Username"
|
||||||
|
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
|
||||||
|
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
|
||||||
|
url="https://management.1984hosting.com/accounts/checkuserauth/"
|
||||||
|
|
||||||
|
response="$(_post "username=$username&password=$password&otpkey=" $url)"
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if _contains "$response" '"loggedin": true'; then
|
||||||
|
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||||
|
export One984HOSTING_SESSIONID_COOKIE
|
||||||
|
export One984HOSTING_CSRFTOKEN_COOKIE
|
||||||
|
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_credentials() {
|
||||||
|
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
|
||||||
|
One984HOSTING_Username=""
|
||||||
|
One984HOSTING_Password=""
|
||||||
|
_err "You haven't specified 1984Hosting username or password yet."
|
||||||
|
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_cookies() {
|
||||||
|
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
|
||||||
|
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
|
||||||
|
_debug "No cached cookie(s) found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authget "https://management.1984hosting.com/accounts/loginstatus/"
|
||||||
|
if _contains "$response" '"ok": true'; then
|
||||||
|
_debug "Cached cookies still valid"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Cached cookies no longer valid"
|
||||||
|
One984HOSTING_SESSIONID_COOKIE=""
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE=""
|
||||||
|
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain="$1"
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authget "https://management.1984hosting.com/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
|
||||||
|
if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain="$h"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#usage: _get_zone_id url domain.com
|
||||||
|
#returns zone id for domain.com
|
||||||
|
_get_zone_id() {
|
||||||
|
url=$1
|
||||||
|
domain=$2
|
||||||
|
_htmlget "$url" "$domain"
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
|
||||||
|
_debug2 _zone_id "$_zone_id"
|
||||||
|
if [ -z "$_zone_id" ]; then
|
||||||
|
_err "Error getting _zone_id for $2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# add extra headers to request
|
||||||
|
_authget() {
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_response=$(_get "$1" | _normalizeJson)
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# truncate huge HTML response
|
||||||
|
# echo: Argument list too long
|
||||||
|
_htmlget() {
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_response=$(_get "$1" | grep "$2")
|
||||||
|
if _contains "$_response" "@$2"; then
|
||||||
|
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# add extra headers to request
|
||||||
|
_authpost() {
|
||||||
|
url="https://management.1984hosting.com/domains"
|
||||||
|
_get_zone_id "$url" "$_domain"
|
||||||
|
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
export _H2="Referer: https://management.1984hosting.com/domains/$_zone_id"
|
||||||
|
export _H3="X-CSRFToken: $csrf_header"
|
||||||
|
_response=$(_post "$1" "$2")
|
||||||
|
}
|
||||||
94
dnsapi/dns_acmedns.sh
Executable file
94
dnsapi/dns_acmedns.sh
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#
|
||||||
|
#Author: Wolfgang Ebner
|
||||||
|
#Author: Sven Neubuaer
|
||||||
|
#Report Bugs here: https://github.com/dampfklon/acme.sh
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
|
||||||
|
#
|
||||||
|
# You can optionally define an already existing account:
|
||||||
|
#
|
||||||
|
# export ACMEDNS_USERNAME="<username>"
|
||||||
|
# export ACMEDNS_PASSWORD="<password>"
|
||||||
|
# export ACMEDNS_SUBDOMAIN="<subdomain>"
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_acmedns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using acme-dns"
|
||||||
|
_debug "fulldomain $fulldomain"
|
||||||
|
_debug "txtvalue $txtvalue"
|
||||||
|
|
||||||
|
#for compatiblity from account conf
|
||||||
|
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readaccountconf_mutable ACMEDNS_USERNAME)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_USERNAME
|
||||||
|
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readaccountconf_mutable ACMEDNS_PASSWORD)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_PASSWORD
|
||||||
|
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readaccountconf_mutable ACMEDNS_SUBDOMAIN)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_SUBDOMAIN
|
||||||
|
|
||||||
|
ACMEDNS_BASE_URL="${ACMEDNS_BASE_URL:-$(_readdomainconf ACMEDNS_BASE_URL)}"
|
||||||
|
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readdomainconf ACMEDNS_USERNAME)}"
|
||||||
|
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readdomainconf ACMEDNS_PASSWORD)}"
|
||||||
|
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readdomainconf ACMEDNS_SUBDOMAIN)}"
|
||||||
|
|
||||||
|
if [ "$ACMEDNS_BASE_URL" = "" ]; then
|
||||||
|
ACMEDNS_BASE_URL="https://auth.acme-dns.io"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACMEDNS_UPDATE_URL="$ACMEDNS_BASE_URL/update"
|
||||||
|
ACMEDNS_REGISTER_URL="$ACMEDNS_BASE_URL/register"
|
||||||
|
|
||||||
|
if [ -z "$ACMEDNS_USERNAME" ] || [ -z "$ACMEDNS_PASSWORD" ]; then
|
||||||
|
response="$(_post "" "$ACMEDNS_REGISTER_URL" "" "POST")"
|
||||||
|
_debug response "$response"
|
||||||
|
ACMEDNS_USERNAME=$(echo "$response" | sed -n 's/^{.*\"username\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received username: $ACMEDNS_USERNAME"
|
||||||
|
ACMEDNS_PASSWORD=$(echo "$response" | sed -n 's/^{.*\"password\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received password: $ACMEDNS_PASSWORD"
|
||||||
|
ACMEDNS_SUBDOMAIN=$(echo "$response" | sed -n 's/^{.*\"subdomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received subdomain: $ACMEDNS_SUBDOMAIN"
|
||||||
|
ACMEDNS_FULLDOMAIN=$(echo "$response" | sed -n 's/^{.*\"fulldomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_info "##########################################################"
|
||||||
|
_info "# Create $fulldomain CNAME $ACMEDNS_FULLDOMAIN DNS entry #"
|
||||||
|
_info "##########################################################"
|
||||||
|
_info "Press enter to continue... "
|
||||||
|
read -r _
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedomainconf ACMEDNS_BASE_URL "$ACMEDNS_BASE_URL"
|
||||||
|
_savedomainconf ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
|
||||||
|
_savedomainconf ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
|
||||||
|
_savedomainconf ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
|
||||||
|
|
||||||
|
export _H1="X-Api-User: $ACMEDNS_USERNAME"
|
||||||
|
export _H2="X-Api-Key: $ACMEDNS_PASSWORD"
|
||||||
|
data="{\"subdomain\":\"$ACMEDNS_SUBDOMAIN\", \"txt\": \"$txtvalue\"}"
|
||||||
|
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$ACMEDNS_UPDATE_URL" "" "POST")"
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
if ! echo "$response" | grep "\"$txtvalue\"" >/dev/null; then
|
||||||
|
_err "invalid response of acme-dns"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_acmedns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using acme-dns"
|
||||||
|
_debug "fulldomain $fulldomain"
|
||||||
|
_debug "txtvalue $txtvalue"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
83
dnsapi/dns_acmeproxy.sh
Normal file
83
dnsapi/dns_acmeproxy.sh
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
## Acmeproxy DNS provider to be used with acmeproxy (http://github.com/mdbraber/acmeproxy)
|
||||||
|
## API integration by Maarten den Braber
|
||||||
|
##
|
||||||
|
## Report any bugs via https://github.com/mdbraber/acme.sh
|
||||||
|
|
||||||
|
dns_acmeproxy_add() {
|
||||||
|
fulldomain="${1}"
|
||||||
|
txtvalue="${2}"
|
||||||
|
action="present"
|
||||||
|
|
||||||
|
_debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'"
|
||||||
|
_acmeproxy_request "$fulldomain" "$txtvalue" "$action"
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_acmeproxy_rm() {
|
||||||
|
fulldomain="${1}"
|
||||||
|
txtvalue="${2}"
|
||||||
|
action="cleanup"
|
||||||
|
|
||||||
|
_debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'"
|
||||||
|
_acmeproxy_request "$fulldomain" "$txtvalue" "$action"
|
||||||
|
}
|
||||||
|
|
||||||
|
_acmeproxy_request() {
|
||||||
|
|
||||||
|
## Nothing to see here, just some housekeeping
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
action=$3
|
||||||
|
|
||||||
|
_info "Using acmeproxy"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
ACMEPROXY_ENDPOINT="${ACMEPROXY_ENDPOINT:-$(_readaccountconf_mutable ACMEPROXY_ENDPOINT)}"
|
||||||
|
ACMEPROXY_USERNAME="${ACMEPROXY_USERNAME:-$(_readaccountconf_mutable ACMEPROXY_USERNAME)}"
|
||||||
|
ACMEPROXY_PASSWORD="${ACMEPROXY_PASSWORD:-$(_readaccountconf_mutable ACMEPROXY_PASSWORD)}"
|
||||||
|
|
||||||
|
## Check for the endpoint
|
||||||
|
if [ -z "$ACMEPROXY_ENDPOINT" ]; then
|
||||||
|
ACMEPROXY_ENDPOINT=""
|
||||||
|
_err "You didn't specify the endpoint"
|
||||||
|
_err "Please set them via 'export ACMEPROXY_ENDPOINT=https://ip:port' and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Save the credentials to the account file
|
||||||
|
_saveaccountconf_mutable ACMEPROXY_ENDPOINT "$ACMEPROXY_ENDPOINT"
|
||||||
|
_saveaccountconf_mutable ACMEPROXY_USERNAME "$ACMEPROXY_USERNAME"
|
||||||
|
_saveaccountconf_mutable ACMEPROXY_PASSWORD "$ACMEPROXY_PASSWORD"
|
||||||
|
|
||||||
|
if [ -z "$ACMEPROXY_USERNAME" ] || [ -z "$ACMEPROXY_PASSWORD" ]; then
|
||||||
|
_info "ACMEPROXY_USERNAME and/or ACMEPROXY_PASSWORD not set - using without client authentication! Make sure you're using server authentication (e.g. IP-based)"
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
else
|
||||||
|
## Base64 encode the credentials
|
||||||
|
credentials=$(printf "%b" "$ACMEPROXY_USERNAME:$ACMEPROXY_PASSWORD" | _base64)
|
||||||
|
|
||||||
|
## Construct the HTTP Authorization header
|
||||||
|
export _H1="Authorization: Basic $credentials"
|
||||||
|
export _H2="Accept: application/json"
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Add the challenge record to the acmeproxy grid member
|
||||||
|
response="$(_post "{\"fqdn\": \"$fulldomain.\", \"value\": \"$txtvalue\"}" "$ACMEPROXY_ENDPOINT/$action" "" "POST")"
|
||||||
|
|
||||||
|
## Let's see if we get something intelligible back from the unit
|
||||||
|
if echo "$response" | grep "\"$txtvalue\"" >/dev/null; then
|
||||||
|
_info "Successfully updated the txt record"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error encountered during record addition"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
141
dnsapi/dns_active24.sh
Executable file
141
dnsapi/dns_active24.sh
Executable file
@@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
|
||||||
|
ACTIVE24_Api="https://api.active24.com"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_active24_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_active24_init
|
||||||
|
|
||||||
|
_info "Adding txt record"
|
||||||
|
if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
|
||||||
|
if _contains "$response" "errors"; then
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_active24_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_active24_init
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_active24_rest GET "dns/$_domain/records/v1"
|
||||||
|
|
||||||
|
if _contains "$response" "errors"; then
|
||||||
|
_err "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
|
||||||
|
|
||||||
|
for hash_id in $hash_ids; do
|
||||||
|
_debug "Removing hash_id" "$hash_id"
|
||||||
|
if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
|
||||||
|
if _contains "$response" "errors"; then
|
||||||
|
_err "Unable to remove txt record."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_info "Removed txt record."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
_err "No txt records found."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
|
||||||
|
if ! _active24_rest GET "dns/domains/v1"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug "h" "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_active24_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $ACTIVE24_Token"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug "data" "$data"
|
||||||
|
response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ACTIVE24_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_active24_init() {
|
||||||
|
ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
|
||||||
|
if [ -z "$ACTIVE24_Token" ]; then
|
||||||
|
ACTIVE24_Token=""
|
||||||
|
_err "You didn't specify a Active24 api token yet."
|
||||||
|
_err "Please create the token and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_Token"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
}
|
||||||
147
dnsapi/dns_ad.sh
Executable file
147
dnsapi/dns_ad.sh
Executable file
@@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
|
||||||
|
#This is the Alwaysdata api wrapper for acme.sh
|
||||||
|
#
|
||||||
|
#Author: Paul Koppen
|
||||||
|
#Report Bugs here: https://github.com/wpk-/acme.sh
|
||||||
|
|
||||||
|
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_ad_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$AD_API_KEY" ]; then
|
||||||
|
AD_API_KEY=""
|
||||||
|
_err "You didn't specify the AD api key yet."
|
||||||
|
_err "Please create you key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf AD_API_KEY "$AD_API_KEY"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"
|
||||||
|
|
||||||
|
if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then
|
||||||
|
_info "txt record updated success."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_ad_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain"
|
||||||
|
|
||||||
|
if [ -n "$response" ]; then
|
||||||
|
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _ad_rest DELETE "record/$record_id/" && [ -z "$response" ]; then
|
||||||
|
_info "txt record deleted success."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=12345
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if _ad_rest GET "domain/"; then
|
||||||
|
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")"
|
||||||
|
if [ "$hostedzone" ]; then
|
||||||
|
_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
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#method uri qstr data
|
||||||
|
_ad_rest() {
|
||||||
|
mtd="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
|
||||||
|
_debug mtd "$mtd"
|
||||||
|
_debug ep "$ep"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$mtd" != "GET" ]; then
|
||||||
|
# both POST and DELETE.
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$AD_API_URL/$ep" "" "$mtd")"
|
||||||
|
else
|
||||||
|
response="$(_get "$AD_API_URL/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
203
dnsapi/dns_ali.sh
Executable file
203
dnsapi/dns_ali.sh
Executable file
@@ -0,0 +1,203 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
Ali_API="https://alidns.aliyuncs.com/"
|
||||||
|
|
||||||
|
#Ali_Key="LTqIA87hOKdjevsf5"
|
||||||
|
#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
|
||||||
|
|
||||||
|
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_ali_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Add record"
|
||||||
|
_add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _ali_rest "Add record"
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_ali_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
||||||
|
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_clean
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_describe_records_query "$h"
|
||||||
|
if ! _ali_rest "Get root" "ignore"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "PageNumber"; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_domain="$h"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p="$i"
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
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() {
|
||||||
|
_qdomain="$1"
|
||||||
|
_qsubdomain="$2"
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
|
query=$query'&DomainName='$_qdomain
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&RRKeyWord='$_qsubdomain
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&TypeKeyWord=TXT'
|
||||||
|
query=$query'&Version=2015-01-09'
|
||||||
|
}
|
||||||
|
|
||||||
|
_add_record_query() {
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=AddDomainRecord'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&RR='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Type=TXT'
|
||||||
|
query=$query'&Value='$3
|
||||||
|
query=$query'&Version=2015-01-09'
|
||||||
|
}
|
||||||
|
|
||||||
|
_delete_record_query() {
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=DeleteDomainRecord'
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&RecordId='$1
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2015-01-09'
|
||||||
|
}
|
||||||
|
|
||||||
|
_describe_records_query() {
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2015-01-09'
|
||||||
|
}
|
||||||
|
|
||||||
|
_clean() {
|
||||||
|
_check_exist_query "$_domain" "$_sub_domain"
|
||||||
|
# do not correct grammar here
|
||||||
|
if ! _ali_rest "Check exist records" "ignore"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep -- "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
|
||||||
|
_debug2 record_id "$record_id"
|
||||||
|
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_debug "record not found, skip"
|
||||||
|
else
|
||||||
|
_delete_record_query "$record_id"
|
||||||
|
_ali_rest "Delete record $record_id" "ignore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_timestamp() {
|
||||||
|
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
||||||
|
}
|
||||||
150
dnsapi/dns_anx.sh
Normal file
150
dnsapi/dns_anx.sh
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Anexia CloudDNS acme.sh hook
|
||||||
|
# Author: MA
|
||||||
|
|
||||||
|
#ANX_Token="xxxx"
|
||||||
|
|
||||||
|
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
dns_anx_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using ANX CDNS API"
|
||||||
|
|
||||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if [ "$ANX_Token" ]; then
|
||||||
|
_saveaccountconf_mutable ANX_Token "$ANX_Token"
|
||||||
|
else
|
||||||
|
_err "You didn't specify a ANEXIA Engine API token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always add records, wildcard need two records with the same name
|
||||||
|
_anx_rest POST "zone.json/${_domain}/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"rdata\":\"$txtvalue\"}"
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_anx_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using ANX CDNS API"
|
||||||
|
|
||||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
|
||||||
|
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_get_record_id
|
||||||
|
|
||||||
|
if _is_uuid "$_record_id"; then
|
||||||
|
if ! _anx_rest DELETE "zone.json/${_domain}/records/$_record_id"; then
|
||||||
|
_err "Delete record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "No record found."
|
||||||
|
fi
|
||||||
|
echo "$response" | tr -d " " | grep \"status\":\"OK\" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_is_uuid() {
|
||||||
|
pattern='^\{?[A-Z0-9a-z]{8}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{12}\}?$'
|
||||||
|
if echo "$1" | _egrep_o "$pattern" >/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_record_id() {
|
||||||
|
_debug subdomain "$_sub_domain"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
|
||||||
|
if _anx_rest GET "zone.json/${_domain}/records?name=$_sub_domain&type=TXT"; then
|
||||||
|
_debug response "$response"
|
||||||
|
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
|
||||||
|
_record_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"identifier\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
else
|
||||||
|
_record_id=''
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Search existing record"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_anx_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Token $ANX_Token"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "${ANX_API}/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "${ANX_API}/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
_anx_rest GET "zone.json"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
150
dnsapi/dns_arvan.sh
Normal file
150
dnsapi/dns_arvan.sh
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Arvan_Token="Apikey xxxx"
|
||||||
|
|
||||||
|
ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
|
||||||
|
#Author: Vahid Fardi
|
||||||
|
#Report Bugs here: https://github.com/Neilpang/acme.sh
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_arvan_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using Arvan"
|
||||||
|
|
||||||
|
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
|
||||||
|
|
||||||
|
if [ -z "$Arvan_Token" ]; then
|
||||||
|
_err "You didn't specify \"Arvan_Token\" token yet."
|
||||||
|
_err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the api token to the account conf file.
|
||||||
|
_saveaccountconf_mutable Arvan_Token "$Arvan_Token"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "response id is $response"
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
elif _contains "$response" "Record Data is Duplicated"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_arvan_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using Arvan"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_arvan_rest GET "${_domain}/dns-records"
|
||||||
|
if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
|
||||||
|
_err "Error on Arvan Api"
|
||||||
|
_err "Please create a github issue with debbug log"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_record_id=$(echo "$response" | _egrep_o ".\"id\":\"[^\"]*\",\"type\":\"txt\",\"name\":\"_acme-challenge\",\"value\":{\"text\":\"$txtvalue\"}" | cut -d : -f 2 | cut -d , -f 1 | tr -d \")
|
||||||
|
if ! _arvan_rest "DELETE" "${_domain}/dns-records/${_record_id}"; then
|
||||||
|
_err "Error on Arvan Api"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "$response"
|
||||||
|
_contains "$response" 'dns record deleted'
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _arvan_rest GET "$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" "\"domain\":\"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_arvan_rest() {
|
||||||
|
mtd="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
|
||||||
|
token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
|
||||||
|
export _H1="Authorization: $token_trimmed"
|
||||||
|
|
||||||
|
if [ "$mtd" = "DELETE" ]; then
|
||||||
|
#DELETE Request shouldn't have Content-Type
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
|
||||||
|
elif [ "$mtd" = "POST" ]; then
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ARVAN_API_URL/$ep$data")"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
171
dnsapi/dns_aurora.sh
Normal file
171
dnsapi/dns_aurora.sh
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
#
|
||||||
|
#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
|
||||||
|
AURORA_Api="https://api.auroradns.eu"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_aurora_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
|
||||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$AURORA_Key" ] || [ -z "$AURORA_Secret" ]; then
|
||||||
|
AURORA_Key=""
|
||||||
|
AURORA_Secret=""
|
||||||
|
_err "You didn't specify an Aurora api key and secret yet."
|
||||||
|
_err "You can get yours from here https://cp.pcextreme.nl/auroradns/users."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and secret to the account conf file.
|
||||||
|
_saveaccountconf_mutable AURORA_Key "$AURORA_Key"
|
||||||
|
_saveaccountconf_mutable AURORA_Secret "$AURORA_Secret"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _aurora_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
elif _contains "$response" "RecordExistsError"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_aurora_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
|
||||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting records"
|
||||||
|
_aurora_rest GET "zones/${_domain_id}/records"
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
else
|
||||||
|
records=$(echo "$response" | _normalizeJson | tr -d "[]" | sed "s/},{/}|{/g" | tr "|" "\n")
|
||||||
|
if [ "$(echo "$records" | wc -l)" -le 2 ]; then
|
||||||
|
_err "Can not parse records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
record_id=$(echo "$records" | grep "\"type\": *\"TXT\"" | grep "\"name\": *\"$_sub_domain\"" | grep "\"content\": *\"$txtvalue\"" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
_debug "record_id" "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _aurora_rest DELETE "zones/$_domain_id/records/$record_id"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _aurora_rest GET "zones/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\": \"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_aurora_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
key_trimmed=$(echo "$AURORA_Key" | tr -d '"')
|
||||||
|
secret_trimmed=$(echo "$AURORA_Secret" | tr -d '"')
|
||||||
|
|
||||||
|
timestamp=$(date -u +"%Y%m%dT%H%M%SZ")
|
||||||
|
signature=$(printf "%s/%s%s" "$m" "$ep" "$timestamp" | _hmac sha256 "$(printf "%s" "$secret_trimmed" | _hex_dump | tr -d " ")" | _base64)
|
||||||
|
authorization=$(printf "AuroraDNSv1 %s" "$(printf "%s:%s" "$key_trimmed" "$signature" | _base64)")
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json; charset=UTF-8"
|
||||||
|
export _H2="X-AuroraDNS-Date: $timestamp"
|
||||||
|
export _H3="Authorization: $authorization"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$AURORA_Api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$AURORA_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
264
dnsapi/dns_autodns.sh
Normal file
264
dnsapi/dns_autodns.sh
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This is the InternetX autoDNS xml api wrapper for acme.sh
|
||||||
|
# Author: auerswald@gmail.com
|
||||||
|
# Created: 2018-01-14
|
||||||
|
#
|
||||||
|
# export AUTODNS_USER="username"
|
||||||
|
# export AUTODNS_PASSWORD="password"
|
||||||
|
# export AUTODNS_CONTEXT="context"
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# acme.sh --issue --dns dns_autodns -d example.com
|
||||||
|
|
||||||
|
AUTODNS_API="https://gateway.autodns.com"
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# txtdomain
|
||||||
|
# txt
|
||||||
|
dns_autodns_add() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
|
||||||
|
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
|
||||||
|
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
|
||||||
|
|
||||||
|
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
|
||||||
|
_err "You don't specify autodns user, password and context."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER"
|
||||||
|
_saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD"
|
||||||
|
_saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
|
||||||
|
if ! _get_autodns_zone "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _zone "$_zone"
|
||||||
|
_debug _system_ns "$_system_ns"
|
||||||
|
|
||||||
|
_info "Adding TXT record"
|
||||||
|
|
||||||
|
autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
|
||||||
|
|
||||||
|
if [ "$?" -eq "0" ]; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# txtdomain
|
||||||
|
# txt
|
||||||
|
dns_autodns_rm() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
|
||||||
|
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
|
||||||
|
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
|
||||||
|
|
||||||
|
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
|
||||||
|
_err "You don't specify autodns user, password and context."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
|
||||||
|
if ! _get_autodns_zone "$fulldomain"; then
|
||||||
|
_err "zone not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _zone "$_zone"
|
||||||
|
_debug _system_ns "$_system_ns"
|
||||||
|
|
||||||
|
_info "Delete TXT record"
|
||||||
|
|
||||||
|
autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
|
||||||
|
|
||||||
|
if [ "$?" -eq "0" ]; then
|
||||||
|
_info "Deleted, OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# fulldomain
|
||||||
|
# Returns:
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _zone=domain.com
|
||||||
|
# _system_ns
|
||||||
|
_get_autodns_zone() {
|
||||||
|
domain="$1"
|
||||||
|
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
# not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
autodns_response="$(_autodns_zone_inquire "$h")"
|
||||||
|
|
||||||
|
if [ "$?" -ne "0" ]; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
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)"
|
||||||
|
_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)
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_request_auth_xml() {
|
||||||
|
printf "<auth>
|
||||||
|
<user>%s</user>
|
||||||
|
<password>%s</password>
|
||||||
|
<context>%s</context>
|
||||||
|
</auth>" "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# zone
|
||||||
|
_build_zone_inquire_xml() {
|
||||||
|
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||||
|
<request>
|
||||||
|
%s
|
||||||
|
<task>
|
||||||
|
<code>0205</code>
|
||||||
|
<view>
|
||||||
|
<children>1</children>
|
||||||
|
<limit>1</limit>
|
||||||
|
</view>
|
||||||
|
<where>
|
||||||
|
<key>name</key>
|
||||||
|
<operator>eq</operator>
|
||||||
|
<value>%s</value>
|
||||||
|
</where>
|
||||||
|
</task>
|
||||||
|
</request>" "$(_build_request_auth_xml)" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# zone
|
||||||
|
# subdomain
|
||||||
|
# txtvalue
|
||||||
|
# system_ns
|
||||||
|
_build_zone_update_xml() {
|
||||||
|
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||||
|
<request>
|
||||||
|
%s
|
||||||
|
<task>
|
||||||
|
<code>0202001</code>
|
||||||
|
<default>
|
||||||
|
<rr_add>
|
||||||
|
<name>%s</name>
|
||||||
|
<ttl>600</ttl>
|
||||||
|
<type>TXT</type>
|
||||||
|
<value>%s</value>
|
||||||
|
</rr_add>
|
||||||
|
</default>
|
||||||
|
<zone>
|
||||||
|
<name>%s</name>
|
||||||
|
<system_ns>%s</system_ns>
|
||||||
|
</zone>
|
||||||
|
</task>
|
||||||
|
</request>" "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# zone
|
||||||
|
_autodns_zone_inquire() {
|
||||||
|
request_data="$(_build_zone_inquire_xml "$1")"
|
||||||
|
autodns_response="$(_autodns_api_call "$request_data")"
|
||||||
|
ret="$?"
|
||||||
|
|
||||||
|
printf "%s" "$autodns_response"
|
||||||
|
return "$ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# zone
|
||||||
|
# subdomain
|
||||||
|
# txtvalue
|
||||||
|
# system_ns
|
||||||
|
_autodns_zone_update() {
|
||||||
|
request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
|
||||||
|
autodns_response="$(_autodns_api_call "$request_data")"
|
||||||
|
ret="$?"
|
||||||
|
|
||||||
|
printf "%s" "$autodns_response"
|
||||||
|
return "$ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# zone
|
||||||
|
# subdomain
|
||||||
|
# txtvalue
|
||||||
|
# system_ns
|
||||||
|
_autodns_zone_cleanup() {
|
||||||
|
request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
|
||||||
|
# replace 'rr_add>' with 'rr_rem>' in request_data
|
||||||
|
request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')"
|
||||||
|
autodns_response="$(_autodns_api_call "$request_data")"
|
||||||
|
ret="$?"
|
||||||
|
|
||||||
|
printf "%s" "$autodns_response"
|
||||||
|
return "$ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
# request_data
|
||||||
|
_autodns_api_call() {
|
||||||
|
request_data="$1"
|
||||||
|
|
||||||
|
_debug request_data "$request_data"
|
||||||
|
|
||||||
|
autodns_response="$(_post "$request_data" "$AUTODNS_API")"
|
||||||
|
ret="$?"
|
||||||
|
|
||||||
|
_debug autodns_response "$autodns_response"
|
||||||
|
|
||||||
|
if [ "$ret" -ne "0" ]; then
|
||||||
|
_err "error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$autodns_response" "<type>success</type>" >/dev/null; then
|
||||||
|
_info "success"
|
||||||
|
printf "%s" "$autodns_response"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
365
dnsapi/dns_aws.sh
Executable file
365
dnsapi/dns_aws.sh
Executable file
@@ -0,0 +1,365 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
#
|
||||||
|
#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_URL="https://$AWS_HOST"
|
||||||
|
|
||||||
|
AWS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Amazon-Route53-API"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_aws_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
||||||
|
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
||||||
|
AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
|
||||||
|
|
||||||
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
|
_use_container_role || _use_instance_role
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
|
AWS_ACCESS_KEY_ID=""
|
||||||
|
AWS_SECRET_ACCESS_KEY=""
|
||||||
|
_err "You haven't specified the aws route53 api key id and and api key secret yet."
|
||||||
|
_err "Please create your key and try again. see $(__green $AWS_WIKI)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save for future use, unless using a role which will be fetched as needed
|
||||||
|
if [ -z "$_using_role" ]; then
|
||||||
|
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
|
||||||
|
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
|
||||||
|
_saveaccountconf_mutable AWS_DNS_SLOWRATE "$AWS_DNS_SLOWRATE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Getting existing records for $fulldomain"
|
||||||
|
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "<Name>$fulldomain.</Name>"; then
|
||||||
|
_resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
|
||||||
|
_debug "_resource_record" "$_resource_record"
|
||||||
|
else
|
||||||
|
_debug "single new add"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
|
||||||
|
_info "The TXT record already exists. Skipping."
|
||||||
|
_sleep 1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Adding records"
|
||||||
|
|
||||||
|
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords>$_resource_record<ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
|
||||||
|
|
||||||
|
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||||
|
_info "TXT record updated successfully."
|
||||||
|
if [ -n "$AWS_DNS_SLOWRATE" ]; then
|
||||||
|
_info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
|
||||||
|
_sleep "$AWS_DNS_SLOWRATE"
|
||||||
|
else
|
||||||
|
_sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_aws_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
||||||
|
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
||||||
|
AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
|
||||||
|
|
||||||
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
|
_use_container_role || _use_instance_role
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Getting existing records for $fulldomain"
|
||||||
|
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "<Name>$fulldomain.</Name>"; then
|
||||||
|
_resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
|
||||||
|
_debug "_resource_record" "$_resource_record"
|
||||||
|
else
|
||||||
|
_debug "no records exist, skip"
|
||||||
|
_sleep 1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords>$_resource_record</ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
|
||||||
|
|
||||||
|
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||||
|
_info "TXT record deleted successfully."
|
||||||
|
if [ -n "$AWS_DNS_SLOWRATE" ]; then
|
||||||
|
_info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
|
||||||
|
_sleep "$AWS_DNS_SLOWRATE"
|
||||||
|
else
|
||||||
|
_sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_sleep 1
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if aws_rest GET "2013-04-01/hostedzone"; then
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug2 "Checking domain: $h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
|
||||||
|
_debug "IsTruncated"
|
||||||
|
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug "NextMarker" "$_nextMarker"
|
||||||
|
if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
|
||||||
|
_debug "Truncated request OK"
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
_err "Truncated request error."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
#not valid
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "<Name>$h.</Name>"; then
|
||||||
|
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
|
||||||
|
_debug hostedzone "$hostedzone"
|
||||||
|
if [ "$hostedzone" ]; then
|
||||||
|
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err "Can't find domain with id: $h"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_use_container_role() {
|
||||||
|
# automatically set if running inside ECS
|
||||||
|
if [ -z "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" ]; then
|
||||||
|
_debug "No ECS environment variable detected"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_use_metadata "169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||||
|
}
|
||||||
|
|
||||||
|
_use_instance_role() {
|
||||||
|
_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
|
||||||
|
_debug "Unable to fetch IAM role from instance metadata"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_aws_role=$(_get "$_url" "" 1)
|
||||||
|
_debug "_aws_role" "$_aws_role"
|
||||||
|
_use_metadata "$_url$_aws_role"
|
||||||
|
}
|
||||||
|
|
||||||
|
_use_metadata() {
|
||||||
|
_aws_creds="$(
|
||||||
|
_get "$1" "" 1 |
|
||||||
|
_normalizeJson |
|
||||||
|
tr '{,}' '\n' |
|
||||||
|
while read -r _line; do
|
||||||
|
_key="$(echo "${_line%%:*}" | tr -d '"')"
|
||||||
|
_value="${_line#*:}"
|
||||||
|
_debug3 "_key" "$_key"
|
||||||
|
_secure_debug3 "_value" "$_value"
|
||||||
|
case "$_key" in
|
||||||
|
AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;;
|
||||||
|
SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
|
||||||
|
Token) echo "AWS_SESSION_TOKEN=$_value" ;;
|
||||||
|
esac
|
||||||
|
done |
|
||||||
|
paste -sd' ' -
|
||||||
|
)"
|
||||||
|
_secure_debug "_aws_creds" "$_aws_creds"
|
||||||
|
|
||||||
|
if [ -z "$_aws_creds" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "$_aws_creds"
|
||||||
|
_using_role=true
|
||||||
|
}
|
||||||
|
|
||||||
|
#method uri qstr data
|
||||||
|
aws_rest() {
|
||||||
|
mtd="$1"
|
||||||
|
ep="$2"
|
||||||
|
qsr="$3"
|
||||||
|
data="$4"
|
||||||
|
|
||||||
|
_debug mtd "$mtd"
|
||||||
|
_debug ep "$ep"
|
||||||
|
_debug qsr "$qsr"
|
||||||
|
_debug data "$data"
|
||||||
|
|
||||||
|
CanonicalURI="/$ep"
|
||||||
|
_debug2 CanonicalURI "$CanonicalURI"
|
||||||
|
|
||||||
|
CanonicalQueryString="$qsr"
|
||||||
|
_debug2 CanonicalQueryString "$CanonicalQueryString"
|
||||||
|
|
||||||
|
RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||||
|
_debug2 RequestDate "$RequestDate"
|
||||||
|
|
||||||
|
#RequestDate="20161120T141056Z" ##############
|
||||||
|
|
||||||
|
export _H1="x-amz-date: $RequestDate"
|
||||||
|
|
||||||
|
aws_host="$AWS_HOST"
|
||||||
|
CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
|
||||||
|
SignedHeaders="host;x-amz-date"
|
||||||
|
if [ -n "$AWS_SESSION_TOKEN" ]; then
|
||||||
|
export _H3="x-amz-security-token: $AWS_SESSION_TOKEN"
|
||||||
|
CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
|
||||||
|
SignedHeaders="${SignedHeaders};x-amz-security-token"
|
||||||
|
fi
|
||||||
|
_debug2 CanonicalHeaders "$CanonicalHeaders"
|
||||||
|
_debug2 SignedHeaders "$SignedHeaders"
|
||||||
|
|
||||||
|
RequestPayload="$data"
|
||||||
|
_debug2 RequestPayload "$RequestPayload"
|
||||||
|
|
||||||
|
Hash="sha256"
|
||||||
|
|
||||||
|
CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
|
||||||
|
_debug2 CanonicalRequest "$CanonicalRequest"
|
||||||
|
|
||||||
|
HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
|
||||||
|
_debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
|
||||||
|
|
||||||
|
Algorithm="AWS4-HMAC-SHA256"
|
||||||
|
_debug2 Algorithm "$Algorithm"
|
||||||
|
|
||||||
|
RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
|
||||||
|
_debug2 RequestDateOnly "$RequestDateOnly"
|
||||||
|
|
||||||
|
Region="us-east-1"
|
||||||
|
Service="route53"
|
||||||
|
|
||||||
|
CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
|
||||||
|
_debug2 CredentialScope "$CredentialScope"
|
||||||
|
|
||||||
|
StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
|
||||||
|
|
||||||
|
_debug2 StringToSign "$StringToSign"
|
||||||
|
|
||||||
|
kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
|
||||||
|
|
||||||
|
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
|
||||||
|
|
||||||
|
_secure_debug2 kSecret "$kSecret"
|
||||||
|
|
||||||
|
kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
|
||||||
|
_secure_debug2 kSecretH "$kSecretH"
|
||||||
|
|
||||||
|
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
|
||||||
|
_debug2 kDateH "$kDateH"
|
||||||
|
|
||||||
|
kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
|
||||||
|
_debug2 kRegionH "$kRegionH"
|
||||||
|
|
||||||
|
kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
|
||||||
|
_debug2 kServiceH "$kServiceH"
|
||||||
|
|
||||||
|
kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)"
|
||||||
|
_debug2 kSigningH "$kSigningH"
|
||||||
|
|
||||||
|
signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
|
||||||
|
_debug2 signature "$signature"
|
||||||
|
|
||||||
|
Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
|
||||||
|
_debug2 Authorization "$Authorization"
|
||||||
|
|
||||||
|
_H2="Authorization: $Authorization"
|
||||||
|
_debug _H2 "$_H2"
|
||||||
|
|
||||||
|
url="$AWS_URL/$ep"
|
||||||
|
if [ "$qsr" ]; then
|
||||||
|
url="$AWS_URL/$ep?$qsr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mtd" = "GET" ]; then
|
||||||
|
response="$(_get "$url")"
|
||||||
|
else
|
||||||
|
response="$(_post "$data" "$url")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ "$_ret" = "0" ]; then
|
||||||
|
if _contains "$response" "<ErrorResponse"; then
|
||||||
|
_err "Response error:$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return "$_ret"
|
||||||
|
}
|
||||||
204
dnsapi/dns_azion.sh
Normal file
204
dnsapi/dns_azion.sh
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
#AZION_Email=""
|
||||||
|
#AZION_Password=""
|
||||||
|
#
|
||||||
|
|
||||||
|
AZION_Api="https://api.azionapi.net"
|
||||||
|
|
||||||
|
######## Public functions ########
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_azion_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "Detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Domain not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
_info "Add or update record"
|
||||||
|
_get_record "$_domain_id" "$_sub_domain"
|
||||||
|
if [ "$record_id" ]; then
|
||||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [$answers_list, \"$txtvalue\"], \"ttl\": 20}"
|
||||||
|
if _azion_rest PUT "intelligent_dns/$_domain_id/records/$record_id" "$_payload"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Record updated."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [\"$txtvalue\"], \"ttl\": 20}"
|
||||||
|
if _azion_rest POST "intelligent_dns/$_domain_id/records" "$_payload"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Record added."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Failed to add or update record."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_azion_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "Detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Domain not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
_info "Removing record"
|
||||||
|
_get_record "$_domain_id" "$_sub_domain"
|
||||||
|
if [ "$record_id" ]; then
|
||||||
|
if _azion_rest DELETE "intelligent_dns/$_domain_id/records/$record_id"; then
|
||||||
|
_info "Record removed."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Failed to remove record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "Record not found or already removed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
# Usage: _acme-challenge.www.domain.com
|
||||||
|
# returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if ! _azion_rest GET "intelligent_dns"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
# not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"domain\":\"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_record() {
|
||||||
|
_domain_id=$1
|
||||||
|
_record=$2
|
||||||
|
|
||||||
|
if ! _azion_rest GET "intelligent_dns/$_domain_id/records"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"entry\":\"$_record\""; then
|
||||||
|
_json_record=$(echo "$response" | tr '{' "\n" | grep "\"entry\":\"$_record\"")
|
||||||
|
if [ "$_json_record" ]; then
|
||||||
|
record_id=$(echo "$_json_record" | _egrep_o "\"record_id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
answers_list=$(echo "$_json_record" | _egrep_o "\"answers_list\":\[.*\]" | _head_n 1 | cut -d : -f 2 | tr -d \[\])
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_token() {
|
||||||
|
AZION_Email="${AZION_Email:-$(_readaccountconf_mutable AZION_Email)}"
|
||||||
|
AZION_Password="${AZION_Password:-$(_readaccountconf_mutable AZION_Password)}"
|
||||||
|
|
||||||
|
if ! _contains "$AZION_Email" "@"; then
|
||||||
|
_err "It seems that the AZION_Email is not a valid email address. Revalidate your environments."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZION_Email" ] || [ -z "$AZION_Password" ]; then
|
||||||
|
_err "You didn't specified a AZION_Email/AZION_Password to generate Azion token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable AZION_Email "$AZION_Email"
|
||||||
|
_saveaccountconf_mutable AZION_Password "$AZION_Password"
|
||||||
|
|
||||||
|
_basic_auth=$(printf "%s:%s" "$AZION_Email" "$AZION_Password" | _base64)
|
||||||
|
_debug _basic_auth "$_basic_auth"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json; version=3"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
export _H3="Authorization: Basic $_basic_auth"
|
||||||
|
|
||||||
|
response="$(_post "" "$AZION_Api/tokens" "" "POST")"
|
||||||
|
if _contains "$response" "\"token\":\"" >/dev/null; then
|
||||||
|
_azion_token=$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
|
export AZION_Token="$_azion_token"
|
||||||
|
else
|
||||||
|
_err "Failed to generate Azion token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_azion_rest() {
|
||||||
|
_method=$1
|
||||||
|
_uri="$2"
|
||||||
|
_data="$3"
|
||||||
|
|
||||||
|
if [ -z "$AZION_Token" ]; then
|
||||||
|
_get_token
|
||||||
|
fi
|
||||||
|
_debug2 token "$AZION_Token"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json; version=3"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
export _H3="Authorization: token $AZION_Token"
|
||||||
|
|
||||||
|
if [ "$_method" != "GET" ]; then
|
||||||
|
_debug _data "$_data"
|
||||||
|
response="$(_post "$_data" "$AZION_Api/$_uri" "" "$_method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$AZION_Api/$_uri")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $_method $_uri $_data"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
378
dnsapi/dns_azure.sh
Normal file
378
dnsapi/dns_azure.sh
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
#
|
||||||
|
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
|
||||||
|
#
|
||||||
|
|
||||||
|
dns_azure_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
||||||
|
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Subscription ID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save subscription id to account conf file.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
|
||||||
|
|
||||||
|
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
|
||||||
|
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
||||||
|
_info "Using Azure managed identity"
|
||||||
|
#save managed identity as preferred authentication method, clear service principal credentials from conf file.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "$AZUREDNS_MANAGEDIDENTITY"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_TENANTID ""
|
||||||
|
_saveaccountconf_mutable AZUREDNS_APPID ""
|
||||||
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
|
||||||
|
else
|
||||||
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
||||||
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Tenant ID "
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_APPID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure App ID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Client Secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save account details to account conf file, don't opt in for azure manages identity check.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "false"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
|
||||||
|
_debug "$acmeRecordURI"
|
||||||
|
# Get existing TXT record
|
||||||
|
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
|
||||||
|
values="{\"value\":[\"$txtvalue\"]}"
|
||||||
|
timestamp="$(_time)"
|
||||||
|
if [ "$_code" = "200" ]; then
|
||||||
|
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"")"
|
||||||
|
_debug "existing TXT found"
|
||||||
|
_debug "$vlist"
|
||||||
|
existingts="$(echo "$response" | _egrep_o "\"acmetscheck\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")"
|
||||||
|
if [ -z "$existingts" ]; then
|
||||||
|
# the record was not created by acme.sh. Copy the exisiting entires
|
||||||
|
existingts=$timestamp
|
||||||
|
fi
|
||||||
|
_diff="$(_math "$timestamp - $existingts")"
|
||||||
|
_debug "existing txt age: $_diff"
|
||||||
|
# only use recently added records and discard if older than 2 hours because they are probably orphaned
|
||||||
|
if [ "$_diff" -lt 7200 ]; then
|
||||||
|
_debug "existing txt value: $vlist"
|
||||||
|
for v in $vlist; do
|
||||||
|
values="$values ,{\"value\":[\"$v\"]}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Add the txtvalue TXT Record
|
||||||
|
body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}"
|
||||||
|
_azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
|
||||||
|
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
|
||||||
|
_info "validation value added"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "error adding validation value ($_code)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
#
|
||||||
|
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
|
||||||
|
#
|
||||||
|
dns_azure_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
||||||
|
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Subscription ID "
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
|
||||||
|
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
||||||
|
_info "Using Azure managed identity"
|
||||||
|
else
|
||||||
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
||||||
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Tenant ID "
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_APPID" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure App ID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
|
||||||
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
|
AZUREDNS_TENANTID=""
|
||||||
|
AZUREDNS_APPID=""
|
||||||
|
AZUREDNS_CLIENTSECRET=""
|
||||||
|
_err "You didn't specify the Azure Client Secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
|
||||||
|
_debug "$acmeRecordURI"
|
||||||
|
# Get existing TXT record
|
||||||
|
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
|
||||||
|
timestamp="$(_time)"
|
||||||
|
if [ "$_code" = "200" ]; then
|
||||||
|
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v -- "$txtvalue")"
|
||||||
|
values=""
|
||||||
|
comma=""
|
||||||
|
for v in $vlist; do
|
||||||
|
values="$values$comma{\"value\":[\"$v\"]}"
|
||||||
|
comma=","
|
||||||
|
done
|
||||||
|
if [ -z "$values" ]; then
|
||||||
|
# No values left remove record
|
||||||
|
_debug "removing validation record completely $acmeRecordURI"
|
||||||
|
_azure_rest DELETE "$acmeRecordURI" "" "$accesstoken"
|
||||||
|
if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
|
||||||
|
_info "validation record removed"
|
||||||
|
else
|
||||||
|
_err "error removing validation record ($_code)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Remove only txtvalue from the TXT Record
|
||||||
|
body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}"
|
||||||
|
_azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
|
||||||
|
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
|
||||||
|
_info "validation value removed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "error removing validation value ($_code)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
################### Private functions below ##################################
|
||||||
|
|
||||||
|
_azure_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
accesstoken="$4"
|
||||||
|
|
||||||
|
MAX_REQUEST_RETRY_TIMES=5
|
||||||
|
_request_retry_times=0
|
||||||
|
while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
|
||||||
|
_debug3 _request_retry_times "$_request_retry_times"
|
||||||
|
export _H1="authorization: Bearer $accesstoken"
|
||||||
|
export _H2="accept: application/json"
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
# clear headers from previous request to avoid getting wrong http code on timeouts
|
||||||
|
: >"$HTTP_HEADER"
|
||||||
|
_debug "$ep"
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_secure_debug2 "data $data"
|
||||||
|
response="$(_post "$data" "$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ep")"
|
||||||
|
fi
|
||||||
|
_ret="$?"
|
||||||
|
_secure_debug2 "response $response"
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
_debug "http response code $_code"
|
||||||
|
if [ "$_code" = "401" ]; then
|
||||||
|
# we have an invalid access token set to expired
|
||||||
|
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
|
||||||
|
_err "access denied make sure your Azure settings are correct. See $WIKI"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# See https://docs.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
|
||||||
|
_request_retry_times="$(_math "$_request_retry_times" + 1)"
|
||||||
|
_info "REST call error $_code retrying $ep in $_request_retry_times s"
|
||||||
|
_sleep "$_request_retry_times"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
done
|
||||||
|
if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then
|
||||||
|
_err "Error Azure REST called was retried $MAX_REQUEST_RETRY_TIMES times."
|
||||||
|
_err "Calling $ep failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
|
||||||
|
_azure_getaccess_token() {
|
||||||
|
managedIdentity=$1
|
||||||
|
tenantID=$2
|
||||||
|
clientID=$3
|
||||||
|
clientSecret=$4
|
||||||
|
|
||||||
|
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
||||||
|
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
|
||||||
|
|
||||||
|
# can we reuse the bearer token?
|
||||||
|
if [ -n "$accesstoken" ] && [ -n "$expires_on" ]; then
|
||||||
|
if [ "$(_time)" -lt "$expires_on" ]; then
|
||||||
|
# brearer token is still valid - reuse it
|
||||||
|
_debug "reusing bearer token"
|
||||||
|
printf "%s" "$accesstoken"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug "bearer token expired"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_debug "getting new bearer token"
|
||||||
|
|
||||||
|
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
|
||||||
|
export _H1="Metadata: true"
|
||||||
|
response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
else
|
||||||
|
export _H1="accept: application/json"
|
||||||
|
export _H2="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
|
||||||
|
_secure_debug2 "data $body"
|
||||||
|
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
|
||||||
|
_ret="$?"
|
||||||
|
_secure_debug2 "response $response"
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$accesstoken" ]; then
|
||||||
|
_err "no acccess token received. Check your Azure settings see $WIKI"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "error $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
|
||||||
|
printf "%s" "$accesstoken"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
subscriptionId=$2
|
||||||
|
accesstoken=$3
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
|
||||||
|
## returns up to 100 zones in one response therefore handling more results is not not implemented
|
||||||
|
## (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
|
||||||
|
##
|
||||||
|
_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
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug2 "Checking domain: $h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
|
_domain_id=$(echo "$response" | _egrep_o "\\{\"id\":\"[^\"]*\\/$h\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
if [ "$i" = 1 ]; then
|
||||||
|
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
|
||||||
|
_sub_domain="@"
|
||||||
|
else
|
||||||
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
|
||||||
|
fi
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
191
dnsapi/dns_cf.sh
191
dnsapi/dns_cf.sh
@@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
#
|
#
|
||||||
#CF_Email="xxxx@sss.com"
|
#CF_Email="xxxx@sss.com"
|
||||||
|
|
||||||
|
#CF_Token="xxxx"
|
||||||
|
#CF_Account_ID="xxxx"
|
||||||
|
#CF_Zone_ID="xxxx"
|
||||||
|
|
||||||
CF_Api="https://api.cloudflare.com/client/v4"
|
CF_Api="https://api.cloudflare.com/client/v4"
|
||||||
|
|
||||||
@@ -16,18 +18,37 @@ dns_cf_add(){
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
|
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
||||||
|
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
||||||
|
CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
|
||||||
|
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
||||||
|
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
||||||
|
|
||||||
|
if [ "$CF_Token" ]; then
|
||||||
|
_saveaccountconf_mutable CF_Token "$CF_Token"
|
||||||
|
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
|
||||||
|
_saveaccountconf_mutable CF_Zone_ID "$CF_Zone_ID"
|
||||||
|
else
|
||||||
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
|
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
|
||||||
_err "You don't specify cloudflare api key and email yet."
|
CF_Key=""
|
||||||
_err "Please create you key and try again."
|
CF_Email=""
|
||||||
|
_err "You didn't specify a Cloudflare api key and email yet."
|
||||||
|
_err "You can get yours from here https://dash.cloudflare.com/profile."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$CF_Email" "@"; then
|
||||||
|
_err "It seems that the CF_Email=$CF_Email is not a valid email address."
|
||||||
|
_err "Please check and retry."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
#save the api key and email to the account conf file.
|
#save the api key and email to the account conf file.
|
||||||
_saveaccountconf CF_Key "$CF_Key"
|
_saveaccountconf_mutable CF_Key "$CF_Key"
|
||||||
_saveaccountconf CF_Email "$CF_Email"
|
_saveaccountconf_mutable CF_Email "$CF_Email"
|
||||||
|
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"
|
_err "invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -38,20 +59,23 @@ dns_cf_add(){
|
|||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
||||||
|
|
||||||
if ! printf "$response" | grep \"success\":true > /dev/null ; then
|
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
_err "Error"
|
_err "Error"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
count=$(printf "$response" | grep -o \"count\":[^,]* | cut -d : -f 2)
|
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
|
||||||
_debug count "$count"
|
# we can not use updating anymore.
|
||||||
if [ "$count" = "0" ] ; then
|
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
|
||||||
|
# _debug count "$count"
|
||||||
|
# if [ "$count" = "0" ]; then
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||||
if printf $response | grep $fulldomain > /dev/null ; then
|
if _contains "$response" "$txtvalue"; then
|
||||||
_info "Added, sleeping 10 seconds"
|
_info "Added, OK"
|
||||||
sleep 10
|
return 0
|
||||||
#todo: check if the record takes effect
|
elif _contains "$response" "The record already exists"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
_err "Add txt record error."
|
_err "Add txt record error."
|
||||||
@@ -59,29 +83,59 @@ dns_cf_add(){
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
_err "Add txt record error."
|
_err "Add txt record error."
|
||||||
else
|
|
||||||
_info "Updating record"
|
|
||||||
record_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \"| head -1)
|
|
||||||
_debug "record_id" $record_id
|
|
||||||
|
|
||||||
_cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
|
|
||||||
if [ "$?" = "0" ]; then
|
|
||||||
_info "Updated, sleeping 10 seconds"
|
|
||||||
sleep 10
|
|
||||||
#todo: check if the record takes effect
|
|
||||||
return 0;
|
|
||||||
fi
|
|
||||||
_err "Update error"
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_cf_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
||||||
|
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
||||||
|
CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
|
||||||
|
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
||||||
|
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
|
||||||
|
|
||||||
|
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
|
_err "Error: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
|
||||||
|
_debug count "$count"
|
||||||
|
if [ "$count" = "0" ]; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
else
|
||||||
|
record_id=$(echo "$response" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
_debug "record_id" "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$response" | tr -d " " | grep \"success\":true >/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################### Private functions bellow ##################################
|
|
||||||
#_acme-challenge.www.domain.com
|
#_acme-challenge.www.domain.com
|
||||||
#returns
|
#returns
|
||||||
# _sub_domain=_acme-challenge.www
|
# _sub_domain=_acme-challenge.www
|
||||||
@@ -89,30 +143,59 @@ dns_cf_add(){
|
|||||||
# _domain_id=sdjkglgdfewsdfg
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while [ '1' ] ; do
|
|
||||||
h=$(printf $domain | cut -d . -f $i-100)
|
# Use Zone ID directly if provided
|
||||||
if [ -z "$h" ] ; then
|
if [ "$CF_Zone_ID" ]; then
|
||||||
#not valid
|
if ! _cf_rest GET "zones/$CF_Zone_ID"; then
|
||||||
return 1;
|
return 1
|
||||||
|
else
|
||||||
|
if echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
|
_domain=$(echo "$response" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
if [ "$_domain" ]; then
|
||||||
|
_cutlength=$((${#domain} - ${#_domain} - 1))
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
|
||||||
|
_domain_id=$CF_Zone_ID
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _cf_rest GET "zones?name=$h" ; then
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if printf $response | grep \"name\":\"$h\" >/dev/null ; then
|
if [ "$CF_Account_ID" ]; then
|
||||||
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | head -1 | cut -d : -f 2 | tr -d \")
|
if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! _cf_rest GET "zones?name=$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
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 " ")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf $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
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
p=$i
|
p=$i
|
||||||
i=$(expr $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -121,15 +204,23 @@ _cf_rest() {
|
|||||||
m=$1
|
m=$1
|
||||||
ep="$2"
|
ep="$2"
|
||||||
data="$3"
|
data="$3"
|
||||||
_debug $ep
|
_debug "$ep"
|
||||||
|
|
||||||
_H1="X-Auth-Email: $CF_Email"
|
email_trimmed=$(echo "$CF_Email" | tr -d '"')
|
||||||
_H2="X-Auth-Key: $CF_Key"
|
key_trimmed=$(echo "$CF_Key" | tr -d '"')
|
||||||
_H3="Content-Type: application/json"
|
token_trimmed=$(echo "$CF_Token" | tr -d '"')
|
||||||
|
|
||||||
if [ "$data" ] ; then
|
export _H1="Content-Type: application/json"
|
||||||
|
if [ "$token_trimmed" ]; then
|
||||||
|
export _H2="Authorization: Bearer $token_trimmed"
|
||||||
|
else
|
||||||
|
export _H2="X-Auth-Email: $email_trimmed"
|
||||||
|
export _H3="X-Auth-Key: $key_trimmed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
_debug data "$data"
|
_debug data "$data"
|
||||||
response="$(_post "$data" "$CF_Api/$ep" "" $m)"
|
response="$(_post "$data" "$CF_Api/$ep" "" "$m")"
|
||||||
else
|
else
|
||||||
response="$(_get "$CF_Api/$ep")"
|
response="$(_get "$CF_Api/$ep")"
|
||||||
fi
|
fi
|
||||||
@@ -141,5 +232,3 @@ _cf_rest() {
|
|||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
197
dnsapi/dns_clouddns.sh
Executable file
197
dnsapi/dns_clouddns.sh
Executable file
@@ -0,0 +1,197 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Author: Radek Sprta <sprta@vshosting.cz>
|
||||||
|
|
||||||
|
#CLOUDDNS_EMAIL=XXXXX
|
||||||
|
#CLOUDDNS_PASSWORD="YYYYYYYYY"
|
||||||
|
#CLOUDDNS_CLIENT_ID=XXXXX
|
||||||
|
|
||||||
|
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
|
||||||
|
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_clouddns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "fulldomain" "$fulldomain"
|
||||||
|
|
||||||
|
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
|
||||||
|
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
|
||||||
|
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
|
||||||
|
|
||||||
|
if [ -z "$CLOUDDNS_PASSWORD" ] || [ -z "$CLOUDDNS_EMAIL" ] || [ -z "$CLOUDDNS_CLIENT_ID" ]; then
|
||||||
|
CLOUDDNS_CLIENT_ID=""
|
||||||
|
CLOUDDNS_EMAIL=""
|
||||||
|
CLOUDDNS_PASSWORD=""
|
||||||
|
_err "You didn't specify a CloudDNS password, email and client ID yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _contains "$CLOUDDNS_EMAIL" "@"; then
|
||||||
|
_err "It seems that the CLOUDDNS_EMAIL=$CLOUDDNS_EMAIL is not a valid email address."
|
||||||
|
_err "Please check and retry."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Save CloudDNS client id, email and password to config file
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_CLIENT_ID "$CLOUDDNS_CLIENT_ID"
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_EMAIL "$CLOUDDNS_EMAIL"
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_PASSWORD "$CLOUDDNS_PASSWORD"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
# Add TXT record
|
||||||
|
data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"domainId\":\"$_domain_id\"}"
|
||||||
|
if _clouddns_api POST "record-txt" "$data"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
elif _contains "$response" '"code":4136'; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
else
|
||||||
|
_err "Add TXT record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Publishing record changes"
|
||||||
|
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: rm _acme-challenge.www.domain.com
|
||||||
|
dns_clouddns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
_debug "fulldomain" "$fulldomain"
|
||||||
|
|
||||||
|
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
|
||||||
|
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
|
||||||
|
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
# Get record ID
|
||||||
|
_clouddns_api GET "domain/$_domain_id"
|
||||||
|
if _contains "$response" "lastDomainRecordList"; then
|
||||||
|
re="\"lastDomainRecordList\".*\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
|
||||||
|
_last_domains=$(echo "$response" | _egrep_o "$re")
|
||||||
|
re2="\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
|
||||||
|
_record_id=$(echo "$_last_domains" | _egrep_o "$re2" | _head_n 1 | cut -d : -f 2 | cut -d , -f 1 | tr -d "\"")
|
||||||
|
_debug _record_id "$_record_id"
|
||||||
|
else
|
||||||
|
_err "Could not retrieve record ID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Removing record"
|
||||||
|
if _clouddns_api DELETE "record/$_record_id"; then
|
||||||
|
if _contains "$response" "\"error\":"; then
|
||||||
|
_err "Could not remove record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Publishing record changes"
|
||||||
|
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# Usage: _get_root _acme-challenge.www.domain.com
|
||||||
|
# Returns:
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
|
||||||
|
# Get domain root
|
||||||
|
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}]}"
|
||||||
|
_clouddns_api "POST" "domain/search" "$data"
|
||||||
|
domain_slice="$domain"
|
||||||
|
while [ -z "$domain_root" ]; do
|
||||||
|
if _contains "$response" "\"domainName\":\"$domain_slice\.\""; then
|
||||||
|
domain_root="$domain_slice"
|
||||||
|
_debug domain_root "$domain_root"
|
||||||
|
fi
|
||||||
|
domain_slice="$(echo "$domain_slice" | cut -d . -f 2-)"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Get domain id
|
||||||
|
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}, \
|
||||||
|
{\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}"
|
||||||
|
_clouddns_api "POST" "domain/search" "$data"
|
||||||
|
if _contains "$response" "\"id\":\""; then
|
||||||
|
re='domainType\":\"[^\"]*\",\"id\":\"([^\"]*)\",' # Match domain id
|
||||||
|
_domain_id=$(echo "$response" | _egrep_o "$re" | _head_n 1 | cut -d : -f 3 | tr -d "\",")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | sed "s/.$domain_root//")
|
||||||
|
_domain="$domain_root"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err 'Domain name not found on your CloudDNS account'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _clouddns_api GET domain/search '{"data": "value"}'
|
||||||
|
# Returns:
|
||||||
|
# response='{"message": "api response"}'
|
||||||
|
_clouddns_api() {
|
||||||
|
method=$1
|
||||||
|
endpoint="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug endpoint "$endpoint"
|
||||||
|
|
||||||
|
if [ -z "$CLOUDDNS_TOKEN" ]; then
|
||||||
|
_clouddns_login
|
||||||
|
fi
|
||||||
|
_debug CLOUDDNS_TOKEN "$CLOUDDNS_TOKEN"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Bearer $CLOUDDNS_TOKEN"
|
||||||
|
|
||||||
|
if [ "$method" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$CLOUDDNS_API/$endpoint" "" "$method" | tr -d '\t\r\n ')"
|
||||||
|
else
|
||||||
|
response="$(_get "$CLOUDDNS_API/$endpoint" | tr -d '\t\r\n ')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $endpoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# CLOUDDNS_TOKEN=dslfje2rj23l
|
||||||
|
_clouddns_login() {
|
||||||
|
login_data="{\"email\": \"$CLOUDDNS_EMAIL\", \"password\": \"$CLOUDDNS_PASSWORD\"}"
|
||||||
|
response="$(_post "$login_data" "$CLOUDDNS_LOGIN_API" "" "POST" "Content-Type: application/json")"
|
||||||
|
|
||||||
|
if _contains "$response" "\"accessToken\":\""; then
|
||||||
|
CLOUDDNS_TOKEN=$(echo "$response" | _egrep_o "\"accessToken\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
|
export CLOUDDNS_TOKEN
|
||||||
|
else
|
||||||
|
echo 'Could not get CloudDNS access token; check your credentials'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
208
dnsapi/dns_cloudns.sh
Executable file
208
dnsapi/dns_cloudns.sh
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
DOMAIN_TYPE=
|
||||||
|
DOMAIN_MASTER=
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_cloudns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_cloudns_add() {
|
||||||
|
_info "Using cloudns"
|
||||||
|
|
||||||
|
if ! _dns_cloudns_init_check; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
zone="$(_dns_cloudns_get_zone_name "$1")"
|
||||||
|
if [ -z "$zone" ]; then
|
||||||
|
_err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
host="$(echo "$1" | sed "s/\.$zone\$//")"
|
||||||
|
record=$2
|
||||||
|
|
||||||
|
_debug zone "$zone"
|
||||||
|
_debug host "$host"
|
||||||
|
_debug record "$record"
|
||||||
|
|
||||||
|
_info "Adding the TXT record for $1"
|
||||||
|
_dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60"
|
||||||
|
if ! _contains "$response" "\"status\":\"Success\""; then
|
||||||
|
_err "Record cannot be added."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Added."
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: dns_cloudns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_cloudns_rm() {
|
||||||
|
_info "Using cloudns"
|
||||||
|
|
||||||
|
if ! _dns_cloudns_init_check; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$zone" ]; then
|
||||||
|
zone="$(_dns_cloudns_get_zone_name "$1")"
|
||||||
|
if [ -z "$zone" ]; then
|
||||||
|
_err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
host="$(echo "$1" | sed "s/\.$zone\$//")"
|
||||||
|
record=$2
|
||||||
|
|
||||||
|
_dns_cloudns_get_zone_info "$zone"
|
||||||
|
|
||||||
|
_debug "Type" "$DOMAIN_TYPE"
|
||||||
|
_debug "Cloud Master" "$DOMAIN_MASTER"
|
||||||
|
if _contains "$DOMAIN_TYPE" "cloud"; then
|
||||||
|
zone=$DOMAIN_MASTER
|
||||||
|
fi
|
||||||
|
_debug "ZONE" "$zone"
|
||||||
|
|
||||||
|
_dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT"
|
||||||
|
if ! _contains "$response" "\"id\":"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
|
||||||
|
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
|
||||||
|
|
||||||
|
if [ -n "$record_id" ]; then
|
||||||
|
_debug zone "$zone"
|
||||||
|
_debug host "$host"
|
||||||
|
_debug record "$record"
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
|
||||||
|
_info "Deleting the TXT record for $1"
|
||||||
|
_dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id"
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"status\":\"Success\""; then
|
||||||
|
_err "The TXT record for $1 cannot be deleted."
|
||||||
|
else
|
||||||
|
_info "Deleted."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_dns_cloudns_init_check() {
|
||||||
|
if [ -n "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
|
||||||
|
CLOUDNS_SUB_AUTH_ID="${CLOUDNS_SUB_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_SUB_AUTH_ID)}"
|
||||||
|
CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}"
|
||||||
|
if [ -z "$CLOUDNS_AUTH_ID$CLOUDNS_SUB_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
|
||||||
|
CLOUDNS_AUTH_ID=""
|
||||||
|
CLOUDNS_SUB_AUTH_ID=""
|
||||||
|
CLOUDNS_AUTH_PASSWORD=""
|
||||||
|
_err "You don't specify cloudns api id and password yet."
|
||||||
|
_err "Please create you id and password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CLOUDNS_AUTH_ID" ] && [ -z "$CLOUDNS_SUB_AUTH_ID" ]; then
|
||||||
|
_err "CLOUDNS_AUTH_ID or CLOUDNS_SUB_AUTH_ID is not configured"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
|
||||||
|
_err "CLOUDNS_AUTH_PASSWORD is not configured"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dns_cloudns_http_api_call "dns/login.json" ""
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"status\":\"Success\""; then
|
||||||
|
_err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# save the api id and password to the account conf file.
|
||||||
|
_saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
|
||||||
|
_saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
|
||||||
|
_saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
|
||||||
|
|
||||||
|
CLOUDNS_INIT_CHECK_COMPLETED=1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_cloudns_get_zone_info() {
|
||||||
|
zone=$1
|
||||||
|
_dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zone"
|
||||||
|
if ! _contains "$response" "\"status\":\"Failed\""; then
|
||||||
|
DOMAIN_TYPE=$(echo "$response" | _egrep_o '"type":"[^"]*"' | cut -d : -f 2 | tr -d '"')
|
||||||
|
if _contains "$DOMAIN_TYPE" "cloud"; then
|
||||||
|
DOMAIN_MASTER=$(echo "$response" | _egrep_o '"cloud-master":"[^"]*"' | cut -d : -f 2 | tr -d '"')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_cloudns_get_zone_name() {
|
||||||
|
i=2
|
||||||
|
while true; do
|
||||||
|
zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100)
|
||||||
|
|
||||||
|
if [ -z "$zoneForCheck" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug zoneForCheck "$zoneForCheck"
|
||||||
|
|
||||||
|
_dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck"
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"status\":\"Failed\""; then
|
||||||
|
echo "$zoneForCheck"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_cloudns_http_api_call() {
|
||||||
|
method=$1
|
||||||
|
|
||||||
|
_debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
|
||||||
|
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
|
||||||
|
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
|
||||||
|
|
||||||
|
if [ -n "$CLOUDNS_SUB_AUTH_ID" ]; then
|
||||||
|
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
|
||||||
|
else
|
||||||
|
auth_user="auth-id=$CLOUDNS_AUTH_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
|
||||||
|
else
|
||||||
|
data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
response="$(_get "$CLOUDNS_API/$method?$data")"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
157
dnsapi/dns_cn.sh
Normal file
157
dnsapi/dns_cn.sh
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
|
||||||
|
# created by 5ll and francis
|
||||||
|
|
||||||
|
CN_API="https://beta.api.core-networks.de"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
dns_cn_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _cn_login; then
|
||||||
|
_err "login failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _cn_get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "_sub_domain $_sub_domain"
|
||||||
|
_debug "_domain $_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
curData="{\"name\":\"$_sub_domain\",\"ttl\":120,\"type\":\"TXT\",\"data\":\"$txtvalue\"}"
|
||||||
|
curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/")"
|
||||||
|
|
||||||
|
_debug "curData $curData"
|
||||||
|
_debug "curResult $curResult"
|
||||||
|
|
||||||
|
if _contains "$curResult" ""; then
|
||||||
|
_info "Added, OK"
|
||||||
|
|
||||||
|
if ! _cn_commit; then
|
||||||
|
_err "commiting changes failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
_debug "curData is $curData"
|
||||||
|
_debug "curResult is $curResult"
|
||||||
|
_err "error adding text record, response was $curResult"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_cn_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _cn_login; then
|
||||||
|
_err "login failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _cn_get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deleting record"
|
||||||
|
curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}"
|
||||||
|
curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")"
|
||||||
|
_debug curData is "$curData"
|
||||||
|
|
||||||
|
_info "commiting changes"
|
||||||
|
if ! _cn_commit; then
|
||||||
|
_err "commiting changes failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deletet txt record"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
################### Private functions below ##################################
|
||||||
|
_cn_login() {
|
||||||
|
CN_User="${CN_User:-$(_readaccountconf_mutable CN_User)}"
|
||||||
|
CN_Password="${CN_Password:-$(_readaccountconf_mutable CN_Password)}"
|
||||||
|
if [ -z "$CN_User" ] || [ -z "$CN_Password" ]; then
|
||||||
|
CN_User=""
|
||||||
|
CN_Password=""
|
||||||
|
_err "You must export variables: CN_User and CN_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the config variables to the account conf file.
|
||||||
|
_saveaccountconf_mutable CN_User "$CN_User"
|
||||||
|
_saveaccountconf_mutable CN_Password "$CN_Password"
|
||||||
|
|
||||||
|
_info "Getting an AUTH-Token"
|
||||||
|
curData="{\"login\":\"${CN_User}\",\"password\":\"${CN_Password}\"}"
|
||||||
|
curResult="$(_post "${curData}" "${CN_API}/auth/token")"
|
||||||
|
_debug "Calling _CN_login: '${curData}' '${CN_API}/auth/token'"
|
||||||
|
|
||||||
|
if _contains "${curResult}" '"token":"'; then
|
||||||
|
authToken=$(echo "${curResult}" | cut -d ":" -f2 | cut -d "," -f1 | sed 's/^.\(.*\).$/\1/')
|
||||||
|
export _H1="Authorization: Bearer $authToken"
|
||||||
|
_info "Successfully acquired AUTH-Token"
|
||||||
|
_debug "AUTH-Token: '${authToken}'"
|
||||||
|
_debug "_H1 '${_H1}'"
|
||||||
|
else
|
||||||
|
_err "Couldn't acquire an AUTH-Token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Commit changes
|
||||||
|
_cn_commit() {
|
||||||
|
_info "Commiting changes"
|
||||||
|
_post "" "${CN_API}/dnszones/$h/records/commit"
|
||||||
|
}
|
||||||
|
|
||||||
|
_cn_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
_debug _H1 "${_H1}"
|
||||||
|
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_cn_zonelist="$(_get ${CN_API}/dnszones/)"
|
||||||
|
_debug _cn_zonelist "${_cn_zonelist}"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "something went wrong while getting the zone list"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug "Zonelist does not contain domain - iterating "
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
|
||||||
|
done
|
||||||
|
_err "Zonelist does not contain domain - exiting"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
253
dnsapi/dns_conoha.sh
Executable file
253
dnsapi/dns_conoha.sh
Executable file
@@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_conoha_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_conoha_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using conoha"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug "Check uesrname and password"
|
||||||
|
CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
|
||||||
|
CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
|
||||||
|
CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
|
||||||
|
CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
|
||||||
|
if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
|
||||||
|
CONOHA_Username=""
|
||||||
|
CONOHA_Password=""
|
||||||
|
CONOHA_TenantId=""
|
||||||
|
CONOHA_IdentityServiceApi=""
|
||||||
|
_err "You didn't specify a conoha api username and password yet."
|
||||||
|
_err "Please create the user and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
|
||||||
|
_saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
|
||||||
|
_saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
|
||||||
|
_saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
|
||||||
|
|
||||||
|
if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
|
||||||
|
accesstoken="$(printf "%s" "$token" | sed -n 1p)"
|
||||||
|
CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
body="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"data\":\"$txtvalue\",\"ttl\":60}"
|
||||||
|
if _conoha_rest POST "$CONOHA_Api/v1/domains/$_domain_id/records" "$body" "$accesstoken"; then
|
||||||
|
if _contains "$response" '"data":"'"$txtvalue"'"'; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_conoha_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using conoha"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug "Check uesrname and password"
|
||||||
|
CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
|
||||||
|
CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
|
||||||
|
CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
|
||||||
|
CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
|
||||||
|
if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
|
||||||
|
CONOHA_Username=""
|
||||||
|
CONOHA_Password=""
|
||||||
|
CONOHA_TenantId=""
|
||||||
|
CONOHA_IdentityServiceApi=""
|
||||||
|
_err "You didn't specify a conoha api username and password yet."
|
||||||
|
_err "Please create the user and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
|
||||||
|
_saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
|
||||||
|
_saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
|
||||||
|
_saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
|
||||||
|
|
||||||
|
if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
|
||||||
|
accesstoken="$(printf "%s" "$token" | sed -n 1p)"
|
||||||
|
CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
if ! _conoha_rest GET "$CONOHA_Api/v1/domains/$_domain_id/records" "" "$accesstoken"; then
|
||||||
|
_err "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' |
|
||||||
|
grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" |
|
||||||
|
_head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
|
||||||
|
_info "Removing the txt record"
|
||||||
|
if ! _conoha_rest DELETE "$CONOHA_Api/v1/domains/$_domain_id/records/$record_id" "" "$accesstoken"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_conoha_rest() {
|
||||||
|
m="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
accesstoken="$4"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
if [ -n "$accesstoken" ]; then
|
||||||
|
export _H3="X-Auth-Token: $accesstoken"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "$ep"
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_secure_debug2 data "$data"
|
||||||
|
response="$(_post "$data" "$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ep")"
|
||||||
|
fi
|
||||||
|
_ret="$?"
|
||||||
|
_secure_debug2 response "$response"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
response="$(printf "%s" "$response" | _normalizeJson)"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_conoha_get_accesstoken() {
|
||||||
|
ep="$1"
|
||||||
|
username="$2"
|
||||||
|
password="$3"
|
||||||
|
tenantId="$4"
|
||||||
|
|
||||||
|
accesstoken="$(_readaccountconf_mutable conoha_accesstoken)"
|
||||||
|
expires="$(_readaccountconf_mutable conoha_tokenvalidto)"
|
||||||
|
CONOHA_Api="$(_readaccountconf_mutable conoha_dns_ep)"
|
||||||
|
|
||||||
|
# can we reuse the access token?
|
||||||
|
if [ -n "$accesstoken" ] && [ -n "$expires" ] && [ -n "$CONOHA_Api" ]; then
|
||||||
|
utc_date="$(_utc_date | sed "s/ /T/")"
|
||||||
|
if expr "$utc_date" "<" "$expires" >/dev/null; then
|
||||||
|
# access token is still valid - reuse it
|
||||||
|
_debug "reusing access token"
|
||||||
|
printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug "access token expired"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_debug "getting new access token"
|
||||||
|
|
||||||
|
body="$(printf '{"auth":{"passwordCredentials":{"username":"%s","password":"%s"},"tenantId":"%s"}}' "$username" "$password" "$tenantId")"
|
||||||
|
if ! _conoha_rest POST "$ep" "$body" ""; then
|
||||||
|
_err error "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
accesstoken=$(printf "%s" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
expires=$(printf "%s" "$response" | _egrep_o "\"expires\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2-4 | tr -d \" | tr -d Z) #expect UTC
|
||||||
|
if [ -z "$accesstoken" ] || [ -z "$expires" ]; then
|
||||||
|
_err "no acccess token received. Check your Conoha settings see $WIKI"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable conoha_accesstoken "$accesstoken"
|
||||||
|
_saveaccountconf_mutable conoha_tokenvalidto "$expires"
|
||||||
|
|
||||||
|
CONOHA_Api=$(printf "%s" "$response" | _egrep_o 'publicURL":"'"$CONOHA_DNS_EP_PREFIX_REGEXP"'[^"]*"' | _head_n 1 | cut -d : -f 2-3 | tr -d \")
|
||||||
|
if [ -z "$CONOHA_Api" ]; then
|
||||||
|
_err "failed to get conoha dns endpoint url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable conoha_dns_ep "$CONOHA_Api"
|
||||||
|
|
||||||
|
printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain="$1"
|
||||||
|
ep="$2"
|
||||||
|
accesstoken="$3"
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100).
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _conoha_rest GET "$ep/v1/domains?name=$h" "" "$accesstoken"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
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 \")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
176
dnsapi/dns_constellix.sh
Normal file
176
dnsapi/dns_constellix.sh
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Author: Wout Decre <wout@canodus.be>
|
||||||
|
|
||||||
|
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
|
||||||
|
#CONSTELLIX_Key="XXX"
|
||||||
|
#CONSTELLIX_Secret="XXX"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_constellix_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
|
||||||
|
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
|
||||||
|
_err "You did not specify the Contellix API key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable CONSTELLIX_Key "$CONSTELLIX_Key"
|
||||||
|
_saveaccountconf_mutable CONSTELLIX_Secret "$CONSTELLIX_Secret"
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The TXT record might already exist when working with wildcard certificates. In that case, update the record by adding the new value.
|
||||||
|
_debug "Search TXT record"
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
|
||||||
|
_info "Adding TXT record"
|
||||||
|
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":60,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
|
||||||
|
_info "Added"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error adding TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/${_record_id}"; then
|
||||||
|
_new_rr_values=$(printf "%s\n" "$response" | _egrep_o '"roundRobin":\[[^]]*\]' | sed "s/\]$/,{\"value\":\"${txtvalue}\"}]/")
|
||||||
|
_debug _new_rr_values "$_new_rr_values"
|
||||||
|
_info "Updating TXT record"
|
||||||
|
if _constellix_rest PUT "domains/${_domain_id}/records/TXT/${_record_id}" "{\"name\":\"${_sub_domain}\",\"ttl\":60,${_new_rr_values}}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"Record.*updated successfully\"}" >/dev/null; then
|
||||||
|
_info "Updated"
|
||||||
|
return 0
|
||||||
|
elif printf -- "%s" "$response" | grep "{\"errors\":\[\"Contents are identical\"\]}" >/dev/null; then
|
||||||
|
_info "Already exists, no need to update"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error updating TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_constellix_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
|
||||||
|
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
|
||||||
|
_err "You did not specify the Contellix API key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The TXT record might have been removed already when working with some wildcard certificates.
|
||||||
|
_debug "Search TXT record"
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
|
||||||
|
_info "Removed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_info "Removing TXT record"
|
||||||
|
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
|
||||||
|
_info "Removed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error removing TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
_debug "Detecting root zone"
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _constellix_rest GET "domains/search?exact=$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
|
||||||
|
_domain="$h"
|
||||||
|
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_constellix_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
rdate=$(date +"%s")"000"
|
||||||
|
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
|
||||||
|
|
||||||
|
export _H1="x-cnsdns-apiKey: $CONSTELLIX_Key"
|
||||||
|
export _H2="x-cnsdns-requestDate: $rdate"
|
||||||
|
export _H3="x-cnsdns-hmac: $hmac"
|
||||||
|
export _H4="Accept: application/json"
|
||||||
|
export _H5="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$CONSTELLIX_Api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$CONSTELLIX_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
159
dnsapi/dns_cpanel.sh
Executable file
159
dnsapi/dns_cpanel.sh
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#
|
||||||
|
#Author: Bjarne Saltbaek
|
||||||
|
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732
|
||||||
|
#
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
#
|
||||||
|
# Export CPANEL username,api token and hostname in the following variables
|
||||||
|
#
|
||||||
|
# cPanel_Username=username
|
||||||
|
# cPanel_Apitoken=apitoken
|
||||||
|
# cPanel_Hostname=hostname
|
||||||
|
#
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_cpanel_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Adding TXT record to cPanel based system"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
_debug cPanel_Username "$cPanel_Username"
|
||||||
|
_debug cPanel_Apitoken "$cPanel_Apitoken"
|
||||||
|
_debug cPanel_Hostname "$cPanel_Hostname"
|
||||||
|
|
||||||
|
if ! _cpanel_login; then
|
||||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "No matching root domain for $fulldomain found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# adding entry
|
||||||
|
_info "Adding the entry"
|
||||||
|
stripped_fulldomain=$(echo "$fulldomain" | sed "s/.$_domain//")
|
||||||
|
_debug "Adding $stripped_fulldomain to $_domain zone"
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&domain=$_domain&name=$stripped_fulldomain&type=TXT&txtdata=$txtvalue&ttl=1"
|
||||||
|
if _successful_update; then return 0; fi
|
||||||
|
_err "Couldn't create entry!"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_cpanel_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using cPanel based system"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _cpanel_login; then
|
||||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root; then
|
||||||
|
_err "No matching root domain for $fulldomain found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_findentry "$fulldomain" "$txtvalue"
|
||||||
|
if [ -z "$_id" ]; then
|
||||||
|
_info "Entry doesn't exist, nothing to delete"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Deleting record..."
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&domain=$_domain&line=$_id"
|
||||||
|
# removing entry
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
|
||||||
|
if _successful_update; then return 0; fi
|
||||||
|
_err "Couldn't delete entry!"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_checkcredentials() {
|
||||||
|
cPanel_Username="${cPanel_Username:-$(_readaccountconf_mutable cPanel_Username)}"
|
||||||
|
cPanel_Apitoken="${cPanel_Apitoken:-$(_readaccountconf_mutable cPanel_Apitoken)}"
|
||||||
|
cPanel_Hostname="${cPanel_Hostname:-$(_readaccountconf_mutable cPanel_Hostname)}"
|
||||||
|
|
||||||
|
if [ -z "$cPanel_Username" ] || [ -z "$cPanel_Apitoken" ] || [ -z "$cPanel_Hostname" ]; then
|
||||||
|
cPanel_Username=""
|
||||||
|
cPanel_Apitoken=""
|
||||||
|
cPanel_Hostname=""
|
||||||
|
_err "You haven't specified cPanel username, apitoken and hostname yet."
|
||||||
|
_err "Please add credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable cPanel_Username "$cPanel_Username"
|
||||||
|
_saveaccountconf_mutable cPanel_Apitoken "$cPanel_Apitoken"
|
||||||
|
_saveaccountconf_mutable cPanel_Hostname "$cPanel_Hostname"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_cpanel_login() {
|
||||||
|
if ! _checkcredentials; then return 1; fi
|
||||||
|
|
||||||
|
if ! _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=CustInfo&cpanel_jsonapi_func=displaycontactinfo"; then
|
||||||
|
_err "cPanel login failed for user $cPanel_Username."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_myget() {
|
||||||
|
#Adds auth header to request
|
||||||
|
export _H1="Authorization: cpanel $cPanel_Username:$cPanel_Apitoken"
|
||||||
|
_result=$(_get "$cPanel_Hostname/$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
_myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones'
|
||||||
|
_domains=$(echo "$_result" | sed 's/.*\(zones.*\[\).*/\1/' | cut -d':' -f2 | sed 's/"//g' | sed 's/{//g')
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
_debug "_domains is: $_domains"
|
||||||
|
if [ -z "$_domains" ]; then
|
||||||
|
_err "Primary domain list not found!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
for _domain in $_domains; do
|
||||||
|
_debug "Checking if $fulldomain ends with $_domain"
|
||||||
|
if (_endswith "$fulldomain" "$_domain"); then
|
||||||
|
_debug "Root domain: $_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_successful_update() {
|
||||||
|
if (echo "$_result" | grep -q 'newserial'); then return 0; fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_findentry() {
|
||||||
|
_debug "In _findentry"
|
||||||
|
#returns id of dns entry, if it exists
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain"
|
||||||
|
_id=$(echo "$_result" | sed "s/.*\(line.*$fulldomain.*$txtvalue\).*/\1/" | cut -d ':' -f 2 | cut -d ',' -f 1)
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
_debug "fulldomain. is $fulldomain."
|
||||||
|
_debug "txtvalue is $txtvalue"
|
||||||
|
_debug "_id is: $_id"
|
||||||
|
if [ -n "$_id" ]; then
|
||||||
|
_debug "Entry found with _id=$_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
130
dnsapi/dns_cx.sh
130
dnsapi/dns_cx.sh
@@ -1,15 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
# Cloudxns.com Domain api
|
# CloudXNS Domain api
|
||||||
#
|
#
|
||||||
#CX_Key="1234"
|
#CX_Key="1234"
|
||||||
#
|
#
|
||||||
#CX_Secret="sADDsdasdgdsf"
|
#CX_Secret="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
|
||||||
CX_Api="https://www.cloudxns.net/api2"
|
CX_Api="https://www.cloudxns.net/api2"
|
||||||
|
|
||||||
|
|
||||||
#REST_API
|
#REST_API
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@@ -18,42 +16,46 @@ dns_cx_add() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
|
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
|
||||||
|
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
|
||||||
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
|
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
|
||||||
_err "You don't specify cloudxns.com api key or secret yet."
|
CX_Key=""
|
||||||
|
CX_Secret=""
|
||||||
|
_err "You don't specify cloudxns.net api key or secret yet."
|
||||||
_err "Please create you key and try again."
|
_err "Please create you key and try again."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REST_API=$CX_Api
|
REST_API="$CX_Api"
|
||||||
|
|
||||||
#save the api key and email to the account conf file.
|
#save the api key and email to the account conf file.
|
||||||
_saveaccountconf CX_Key "$CX_Key"
|
_saveaccountconf_mutable CX_Key "$CX_Key"
|
||||||
_saveaccountconf CX_Secret "$CX_Secret"
|
_saveaccountconf_mutable CX_Secret "$CX_Secret"
|
||||||
|
|
||||||
|
|
||||||
_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"
|
_err "invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
existing_records $_domain $_sub_domain
|
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
_debug count "$count"
|
}
|
||||||
if [ "$?" != "0" ] ; then
|
|
||||||
_err "Error get existing records."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$count" = "0" ] ; then
|
#fulldomain txtvalue
|
||||||
add_record $_domain $_sub_domain $txtvalue
|
dns_cx_rm() {
|
||||||
else
|
fulldomain=$1
|
||||||
update_record $_domain $_sub_domain $txtvalue
|
txtvalue=$2
|
||||||
|
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
|
||||||
|
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
|
||||||
|
REST_API="$CX_Api"
|
||||||
|
if _get_root "$fulldomain"; then
|
||||||
|
record_id=""
|
||||||
|
existing_records "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
|
if [ "$record_id" ]; then
|
||||||
|
_rest DELETE "record/$record_id/$_domain_id" "{}"
|
||||||
|
_info "Deleted record ${fulldomain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$?" = "0" ] ; then
|
|
||||||
return 0
|
|
||||||
fi
|
fi
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#usage: root sub
|
#usage: root sub
|
||||||
@@ -64,20 +66,18 @@ existing_records() {
|
|||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
root=$1
|
root=$1
|
||||||
sub=$2
|
sub=$2
|
||||||
|
|
||||||
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
|
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
count=0
|
|
||||||
seg=$(printf "$response" | grep -o "{[^{]*host\":\"$_sub_domain\"[^}]*}")
|
seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
|
||||||
_debug seg "$seg"
|
_debug seg "$seg"
|
||||||
if [ -z "$seg" ]; then
|
if [ -z "$seg" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if printf "$response" | grep '"type":"TXT"' > /dev/null ; then
|
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
|
||||||
count=1
|
record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
|
||||||
record_id=$(printf "$seg" | grep -o \"record_id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
|
||||||
_debug record_id "$record_id"
|
_debug record_id "$record_id"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -90,7 +90,7 @@ add_record() {
|
|||||||
root=$1
|
root=$1
|
||||||
sub=$2
|
sub=$2
|
||||||
txtvalue=$3
|
txtvalue=$3
|
||||||
fulldomain=$sub.$root
|
fulldomain="$sub.$root"
|
||||||
|
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
|
|
||||||
@@ -101,27 +101,7 @@ add_record() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#update the txt record
|
#################### Private functions below ##################################
|
||||||
#Usage: root sub txtvalue
|
|
||||||
update_record() {
|
|
||||||
root=$1
|
|
||||||
sub=$2
|
|
||||||
txtvalue=$3
|
|
||||||
fulldomain=$sub.$root
|
|
||||||
|
|
||||||
_info "Updating record"
|
|
||||||
|
|
||||||
if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}" ; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################### Private functions bellow ##################################
|
|
||||||
#_acme-challenge.www.domain.com
|
#_acme-challenge.www.domain.com
|
||||||
#returns
|
#returns
|
||||||
# _sub_domain=_acme-challenge.www
|
# _sub_domain=_acme-challenge.www
|
||||||
@@ -136,40 +116,39 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while [ '1' ] ; do
|
while true; do
|
||||||
h=$(printf $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
|
||||||
|
|
||||||
if printf "$response" | grep "$h." >/dev/null ; then
|
if _contains "$response" "$h."; then
|
||||||
seg=$(printf "$response" | grep -o "{[^{]*$h\.[^}]*\}" )
|
seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
|
||||||
_debug seg "$seg"
|
_debug seg "$seg"
|
||||||
_domain_id=$(printf "$seg" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$seg" | _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 $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"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
p=$i
|
p="$i"
|
||||||
i=$(expr $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#Usage: method URI data
|
#Usage: method URI data
|
||||||
_rest() {
|
_rest() {
|
||||||
m=$1
|
m=$1
|
||||||
ep="$2"
|
ep="$2"
|
||||||
_debug $ep
|
_debug ep "$ep"
|
||||||
url="$REST_API/$ep"
|
url="$REST_API/$ep"
|
||||||
_debug url "$url"
|
_debug url "$url"
|
||||||
|
|
||||||
@@ -181,16 +160,16 @@ _rest() {
|
|||||||
|
|
||||||
sec="$CX_Key$url$data$cdate$CX_Secret"
|
sec="$CX_Key$url$data$cdate$CX_Secret"
|
||||||
_debug sec "$sec"
|
_debug sec "$sec"
|
||||||
hmac=$(printf "$sec"| openssl md5 |cut -d " " -f 2)
|
hmac=$(printf "%s" "$sec" | _digest md5 hex)
|
||||||
_debug hmac "$hmac"
|
_debug hmac "$hmac"
|
||||||
|
|
||||||
_H1="API-KEY: $CX_Key"
|
export _H1="API-KEY: $CX_Key"
|
||||||
_H2="API-REQUEST-DATE: $cdate"
|
export _H2="API-REQUEST-DATE: $cdate"
|
||||||
_H3="API-HMAC: $hmac"
|
export _H3="API-HMAC: $hmac"
|
||||||
_H4="Content-Type: application/json"
|
export _H4="Content-Type: application/json"
|
||||||
|
|
||||||
if [ "$data" ]; then
|
if [ "$data" ]; then
|
||||||
response="$(_post "$data" "$url" "" $m)"
|
response="$(_post "$data" "$url" "" "$m")"
|
||||||
else
|
else
|
||||||
response="$(_get "$url")"
|
response="$(_get "$url")"
|
||||||
fi
|
fi
|
||||||
@@ -200,10 +179,7 @@ _rest() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
if ! printf "$response" | grep '"message":"success"' > /dev/null ; then
|
|
||||||
return 1
|
_contains "$response" '"code":1'
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
328
dnsapi/dns_cyon.sh
Normal file
328
dnsapi/dns_cyon.sh
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
########
|
||||||
|
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
|
||||||
|
#
|
||||||
|
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# -------------
|
||||||
|
# - oathtool (When using 2 Factor Authentication)
|
||||||
|
#
|
||||||
|
# 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() {
|
||||||
|
_cyon_load_credentials &&
|
||||||
|
_cyon_load_parameters "$@" &&
|
||||||
|
_cyon_print_header "add" &&
|
||||||
|
_cyon_login &&
|
||||||
|
_cyon_change_domain_env &&
|
||||||
|
_cyon_add_txt &&
|
||||||
|
_cyon_logout
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_cyon_rm() {
|
||||||
|
_cyon_load_credentials &&
|
||||||
|
_cyon_load_parameters "$@" &&
|
||||||
|
_cyon_print_header "delete" &&
|
||||||
|
_cyon_login &&
|
||||||
|
_cyon_change_domain_env &&
|
||||||
|
_cyon_delete_txt &&
|
||||||
|
_cyon_logout
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
### PRIVATE FUNCTIONS ###
|
||||||
|
#########################
|
||||||
|
|
||||||
|
_cyon_load_credentials() {
|
||||||
|
# Convert loaded password to/from base64 as needed.
|
||||||
|
if [ "${CY_Password_B64}" ]; then
|
||||||
|
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
|
||||||
|
elif [ "${CY_Password}" ]; then
|
||||||
|
CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then
|
||||||
|
# Dummy entries to satisfy script checker.
|
||||||
|
CY_Username=""
|
||||||
|
CY_Password=""
|
||||||
|
CY_OTP_Secret=""
|
||||||
|
|
||||||
|
_err ""
|
||||||
|
_err "You haven't set your cyon.ch login credentials yet."
|
||||||
|
_err "Please set the required cyon environment variables."
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save the login credentials to the account.conf file.
|
||||||
|
_debug "Save credentials to account.conf"
|
||||||
|
_saveaccountconf CY_Username "${CY_Username}"
|
||||||
|
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
|
||||||
|
if [ -n "${CY_OTP_Secret}" ]; then
|
||||||
|
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
|
||||||
|
else
|
||||||
|
_clearaccountconf CY_OTP_Secret
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_is_idn() {
|
||||||
|
_idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")"
|
||||||
|
_idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")"
|
||||||
|
[ "$_idn_temp" ] || [ "$_idn_temp2" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_load_parameters() {
|
||||||
|
# Read the required parameters to add the TXT entry.
|
||||||
|
# shellcheck disable=SC2018,SC2019
|
||||||
|
fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")"
|
||||||
|
fulldomain_idn="${fulldomain}"
|
||||||
|
|
||||||
|
# Special case for IDNs, as cyon needs a domain environment change,
|
||||||
|
# which uses the "pretty" instead of the punycode version.
|
||||||
|
if _cyon_is_idn "${fulldomain}"; then
|
||||||
|
if ! _exists idn; then
|
||||||
|
_err "Please install idn to process IDN names."
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fulldomain="$(idn -u "${fulldomain}")"
|
||||||
|
fulldomain_idn="$(idn -a "${fulldomain}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug fulldomain "${fulldomain}"
|
||||||
|
_debug fulldomain_idn "${fulldomain_idn}"
|
||||||
|
|
||||||
|
txtvalue="${2}"
|
||||||
|
_debug txtvalue "${txtvalue}"
|
||||||
|
|
||||||
|
# This header is required for curl calls.
|
||||||
|
_H1="X-Requested-With: XMLHttpRequest"
|
||||||
|
export _H1
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_print_header() {
|
||||||
|
if [ "${1}" = "add" ]; then
|
||||||
|
_info ""
|
||||||
|
_info "+---------------------------------------------+"
|
||||||
|
_info "| Adding DNS TXT entry to your cyon.ch domain |"
|
||||||
|
_info "+---------------------------------------------+"
|
||||||
|
_info ""
|
||||||
|
_info " * Full Domain: ${fulldomain}"
|
||||||
|
_info " * TXT Value: ${txtvalue}"
|
||||||
|
_info ""
|
||||||
|
elif [ "${1}" = "delete" ]; then
|
||||||
|
_info ""
|
||||||
|
_info "+-------------------------------------------------+"
|
||||||
|
_info "| Deleting DNS TXT entry from your cyon.ch domain |"
|
||||||
|
_info "+-------------------------------------------------+"
|
||||||
|
_info ""
|
||||||
|
_info " * Full Domain: ${fulldomain}"
|
||||||
|
_info ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_get_cookie_header() {
|
||||||
|
printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')"
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_login() {
|
||||||
|
_info " - Logging in..."
|
||||||
|
|
||||||
|
username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)"
|
||||||
|
password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)"
|
||||||
|
|
||||||
|
login_url="https://my.cyon.ch/auth/index/dologin-async"
|
||||||
|
login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")"
|
||||||
|
|
||||||
|
login_response="$(_post "$login_data" "$login_url")"
|
||||||
|
_debug login_response "${login_response}"
|
||||||
|
|
||||||
|
# Bail if login fails.
|
||||||
|
if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then
|
||||||
|
_err " $(printf "%s" "${login_response}" | _cyon_get_response_message)"
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info " success"
|
||||||
|
|
||||||
|
# NECESSARY!! Load the main page after login, to get the new cookie.
|
||||||
|
_H2="$(_cyon_get_cookie_header)"
|
||||||
|
export _H2
|
||||||
|
|
||||||
|
_get "https://my.cyon.ch/" >/dev/null
|
||||||
|
|
||||||
|
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
|
||||||
|
|
||||||
|
# 2FA authentication with OTP?
|
||||||
|
if [ -n "${CY_OTP_Secret}" ]; then
|
||||||
|
_info " - Authorising with OTP code..."
|
||||||
|
|
||||||
|
if ! _exists oathtool; then
|
||||||
|
_err "Please install oathtool to use 2 Factor Authentication."
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get OTP code with the defined secret.
|
||||||
|
otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)"
|
||||||
|
|
||||||
|
login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async"
|
||||||
|
login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0"
|
||||||
|
|
||||||
|
login_otp_response="$(_post "$login_otp_data" "$login_otp_url")"
|
||||||
|
_debug login_otp_response "${login_otp_response}"
|
||||||
|
|
||||||
|
# Bail if OTP authentication fails.
|
||||||
|
if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then
|
||||||
|
_err " $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)"
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info " success"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_logout() {
|
||||||
|
_info " - Logging out..."
|
||||||
|
|
||||||
|
_get "https://my.cyon.ch/auth/index/dologout" >/dev/null
|
||||||
|
|
||||||
|
_info " success"
|
||||||
|
_info ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_change_domain_env() {
|
||||||
|
_info " - Changing domain environment..."
|
||||||
|
|
||||||
|
# Get the "example.com" part of the full domain name.
|
||||||
|
domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')"
|
||||||
|
_debug "Changing domain environment to ${domain_env}"
|
||||||
|
|
||||||
|
gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")"
|
||||||
|
_debug gloo_item_key "${gloo_item_key}"
|
||||||
|
|
||||||
|
domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}"
|
||||||
|
|
||||||
|
domain_env_response="$(_get "${domain_env_url}")"
|
||||||
|
_debug domain_env_response "${domain_env_response}"
|
||||||
|
|
||||||
|
if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
|
||||||
|
|
||||||
|
domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
|
||||||
|
|
||||||
|
# Bail if domain environment change fails.
|
||||||
|
if [ "${domain_env_success}" != "true" ]; then
|
||||||
|
_err " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info " success"
|
||||||
|
_info ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_add_txt() {
|
||||||
|
_info " - Adding DNS TXT entry..."
|
||||||
|
|
||||||
|
add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
|
||||||
|
add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
|
||||||
|
|
||||||
|
add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
|
||||||
|
_debug add_txt_response "${add_txt_response}"
|
||||||
|
|
||||||
|
if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi
|
||||||
|
|
||||||
|
add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
|
||||||
|
add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
|
||||||
|
|
||||||
|
# Bail if adding TXT entry fails.
|
||||||
|
if [ "${add_txt_status}" != "true" ]; then
|
||||||
|
_err " ${add_txt_message}"
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info " success (TXT|${fulldomain_idn}.|${txtvalue})"
|
||||||
|
_info ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_delete_txt() {
|
||||||
|
_info " - Deleting DNS TXT entry..."
|
||||||
|
|
||||||
|
list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async"
|
||||||
|
|
||||||
|
list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')"
|
||||||
|
_debug list_txt_response "${list_txt_response}"
|
||||||
|
|
||||||
|
if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi
|
||||||
|
|
||||||
|
# Find and delete all acme challenge entries for the $fulldomain.
|
||||||
|
_dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')"
|
||||||
|
|
||||||
|
printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do
|
||||||
|
dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)"
|
||||||
|
dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)"
|
||||||
|
|
||||||
|
if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
hash_encoded="$(printf "%s" "${_hash}" | _url_encode)"
|
||||||
|
identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)"
|
||||||
|
|
||||||
|
delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async"
|
||||||
|
delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")"
|
||||||
|
|
||||||
|
delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")"
|
||||||
|
_debug delete_txt_response "${delete_txt_response}"
|
||||||
|
|
||||||
|
if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi
|
||||||
|
|
||||||
|
delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)"
|
||||||
|
delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)"
|
||||||
|
|
||||||
|
# Skip if deleting TXT entry fails.
|
||||||
|
if [ "${delete_txt_status}" != "true" ]; then
|
||||||
|
_err " ${delete_txt_message} (${_identifier})"
|
||||||
|
else
|
||||||
|
_info " success (${_identifier})"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
_info " done"
|
||||||
|
_info ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_get_response_message() {
|
||||||
|
_egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_get_response_status() {
|
||||||
|
_egrep_o '"status":\w*' | cut -d : -f 2
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_get_response_success() {
|
||||||
|
_egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
_cyon_check_if_2fa_missed() {
|
||||||
|
# Did we miss the 2FA?
|
||||||
|
if test "${1#*multi_factor_form}" != "${1}"; then
|
||||||
|
_err " Missed OTP authentication!"
|
||||||
|
_err ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
184
dnsapi/dns_da.sh
Executable file
184
dnsapi/dns_da.sh
Executable file
@@ -0,0 +1,184 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
||||||
|
# vim: et ts=2 sw=2
|
||||||
|
#
|
||||||
|
# DirectAdmin 1.41.0 API
|
||||||
|
# The DirectAdmin interface has it's own Let's encrypt functionality, but this
|
||||||
|
# script can be used to generate certificates for names which are not hosted on
|
||||||
|
# DirectAdmin
|
||||||
|
#
|
||||||
|
# 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 #####################
|
||||||
|
|
||||||
|
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_da_add() {
|
||||||
|
fulldomain="${1}"
|
||||||
|
txtvalue="${2}"
|
||||||
|
_debug "Calling: dns_da_add() '${fulldomain}' '${txtvalue}'"
|
||||||
|
_DA_credentials && _DA_getDomainInfo && _DA_addTxt
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: dns_da_rm _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_da_rm() {
|
||||||
|
fulldomain="${1}"
|
||||||
|
txtvalue="${2}"
|
||||||
|
_debug "Calling: dns_da_rm() '${fulldomain}' '${txtvalue}'"
|
||||||
|
_DA_credentials && _DA_getDomainInfo && _DA_rmTxt
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
# Usage: _DA_credentials
|
||||||
|
# It will check if the needed settings are available
|
||||||
|
_DA_credentials() {
|
||||||
|
DA_Api="${DA_Api:-$(_readaccountconf_mutable DA_Api)}"
|
||||||
|
DA_Api_Insecure="${DA_Api_Insecure:-$(_readaccountconf_mutable DA_Api_Insecure)}"
|
||||||
|
if [ -z "${DA_Api}" ] || [ -z "${DA_Api_Insecure}" ]; then
|
||||||
|
DA_Api=""
|
||||||
|
DA_Api_Insecure=""
|
||||||
|
_err "You haven't specified the DirectAdmin Login data, URL and whether you want check the DirectAdmin SSL cert. Please try again."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_saveaccountconf_mutable DA_Api "${DA_Api}"
|
||||||
|
_saveaccountconf_mutable DA_Api_Insecure "${DA_Api_Insecure}"
|
||||||
|
# Set whether curl should use secure or insecure mode
|
||||||
|
export HTTPS_INSECURE="${DA_Api_Insecure}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _get_root _acme-challenge.www.example.com
|
||||||
|
# Split the full domain to a domain and subdomain
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=example.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
# Get a list of all the domains
|
||||||
|
# response will contain "list[]=example.com&list[]=example.org"
|
||||||
|
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
# not valid
|
||||||
|
_debug "The given domain $h is not valid"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" "$h" >/dev/null; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
_debug "Stop on 100"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _da_api CMD_API_* data example.com
|
||||||
|
# Use the DirectAdmin API and check the result
|
||||||
|
# returns
|
||||||
|
# response="error=0&text=Result text&details="
|
||||||
|
_da_api() {
|
||||||
|
cmd=$1
|
||||||
|
data=$2
|
||||||
|
domain=$3
|
||||||
|
_debug "$domain; $data"
|
||||||
|
response="$(_post "$data" "$DA_Api/$cmd" "" "POST")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $cmd"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
case "${cmd}" in
|
||||||
|
CMD_API_DNS_CONTROL)
|
||||||
|
# Parse the result in general
|
||||||
|
# error=0&text=Records Deleted&details=
|
||||||
|
# error=1&text=Cannot View Dns Record&details=No domain provided
|
||||||
|
err_field="$(_getfield "$response" 1 '&')"
|
||||||
|
txt_field="$(_getfield "$response" 2 '&')"
|
||||||
|
details_field="$(_getfield "$response" 3 '&')"
|
||||||
|
error="$(_getfield "$err_field" 2 '=')"
|
||||||
|
text="$(_getfield "$txt_field" 2 '=')"
|
||||||
|
details="$(_getfield "$details_field" 2 '=')"
|
||||||
|
_debug "error: ${error}, text: ${text}, details: ${details}"
|
||||||
|
if [ "$error" != "0" ]; then
|
||||||
|
_err "error $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
CMD_API_SHOW_DOMAINS) ;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _DA_getDomainInfo
|
||||||
|
# Get the root zone if possible
|
||||||
|
_DA_getDomainInfo() {
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_debug "The root domain: $_domain"
|
||||||
|
_debug "The sub domain: $_sub_domain"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _DA_addTxt
|
||||||
|
# Use the API to add a record
|
||||||
|
_DA_addTxt() {
|
||||||
|
curData="domain=${_domain}&action=add&type=TXT&name=${_sub_domain}&value=\"${txtvalue}\""
|
||||||
|
_debug "Calling _DA_addTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'"
|
||||||
|
_da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"
|
||||||
|
_debug "Result of _DA_addTxt: '$response'"
|
||||||
|
if _contains "${response}" 'error=0'; then
|
||||||
|
_debug "Add TXT succeeded"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Add TXT failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _DA_rmTxt
|
||||||
|
# Use the API to remove a record
|
||||||
|
_DA_rmTxt() {
|
||||||
|
curData="domain=${_domain}&action=select&txtrecs0=name=${_sub_domain}&value=\"${txtvalue}\""
|
||||||
|
_debug "Calling _DA_rmTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'"
|
||||||
|
if _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"; then
|
||||||
|
_debug "Result of _DA_rmTxt: '$response'"
|
||||||
|
else
|
||||||
|
_err "Result of _DA_rmTxt: '$response'"
|
||||||
|
fi
|
||||||
|
if _contains "${response}" 'error=0'; then
|
||||||
|
_debug "RM TXT succeeded"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "RM TXT failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
130
dnsapi/dns_ddnss.sh
Normal file
130
dnsapi/dns_ddnss.sh
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
||||||
|
#modified by helbgd @ 03/13/2018 to support ddnss.de
|
||||||
|
#modified by mod242 @ 04/24/2018 to support different ddnss domains
|
||||||
|
#Please note: the Wildcard Feature must be turned on for the Host record
|
||||||
|
#and the checkbox for TXT needs to be enabled
|
||||||
|
|
||||||
|
# Pass credentials before "acme.sh --issue --dns dns_ddnss ..."
|
||||||
|
# --
|
||||||
|
# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
|
# --
|
||||||
|
#
|
||||||
|
|
||||||
|
DDNSS_DNS_API="https://ddnss.de/upd.php"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_ddnss_add _acme-challenge.domain.ddnss.de "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_ddnss_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DDNSS_Token="${DDNSS_Token:-$(_readaccountconf_mutable DDNSS_Token)}"
|
||||||
|
if [ -z "$DDNSS_Token" ]; then
|
||||||
|
_err "You must export variable: DDNSS_Token"
|
||||||
|
_err "The token for your DDNSS account is necessary."
|
||||||
|
_err "You can look it up in your DDNSS account."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now save the credentials.
|
||||||
|
_saveaccountconf_mutable DDNSS_Token "$DDNSS_Token"
|
||||||
|
|
||||||
|
# Unfortunately, DDNSS does not seems to support lookup domain through API
|
||||||
|
# So I assume your credentials (which are your domain and token) are correct
|
||||||
|
# If something goes wrong, we will get a KO response from DDNSS
|
||||||
|
|
||||||
|
if ! _ddnss_get_domain; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now add the TXT record to DDNSS DNS
|
||||||
|
_info "Trying to add TXT record"
|
||||||
|
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=1&txt=$txtvalue"; then
|
||||||
|
if [ "$response" = "Updated 1 hostname." ]; then
|
||||||
|
_info "TXT record has been successfully added to your DDNSS domain."
|
||||||
|
_info "Note that all subdomains under this domain uses the same TXT record."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Errors happened during adding the TXT record, response=$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Errors happened during adding the TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_ddnss_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DDNSS_Token="${DDNSS_Token:-$(_readaccountconf_mutable DDNSS_Token)}"
|
||||||
|
if [ -z "$DDNSS_Token" ]; then
|
||||||
|
_err "You must export variable: DDNSS_Token"
|
||||||
|
_err "The token for your DDNSS account is necessary."
|
||||||
|
_err "You can look it up in your DDNSS account."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _ddnss_get_domain; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now remove the TXT record from DDNS DNS
|
||||||
|
_info "Trying to remove TXT record"
|
||||||
|
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=2"; then
|
||||||
|
if [ "$response" = "Updated 1 hostname." ]; then
|
||||||
|
_info "TXT record has been successfully removed from your DDNSS domain."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Errors happened during removing the TXT record, response=$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Errors happened during removing the TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
#fulldomain=_acme-challenge.domain.ddnss.de
|
||||||
|
#returns
|
||||||
|
# _ddnss_domain=domain
|
||||||
|
_ddnss_get_domain() {
|
||||||
|
|
||||||
|
# We'll extract the domain/username from full domain
|
||||||
|
_ddnss_domain="$(echo "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.](ddnss|dyn-ip24|dyndns|dyn|dyndns1|home-webserver|myhome-server|dynip)\..*' | cut -d . -f 2-)"
|
||||||
|
|
||||||
|
if [ -z "$_ddnss_domain" ]; then
|
||||||
|
_err "Error extracting the domain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: method URI
|
||||||
|
_ddnss_rest() {
|
||||||
|
method=$1
|
||||||
|
param="$2"
|
||||||
|
_debug param "$param"
|
||||||
|
url="$DDNSS_DNS_API?$param"
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
# DDNSS uses GET to update domain info
|
||||||
|
if [ "$method" = "GET" ]; then
|
||||||
|
response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | tr -s "\n" | _tail_n 1)"
|
||||||
|
else
|
||||||
|
_err "Unsupported method"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
197
dnsapi/dns_desec.sh
Normal file
197
dnsapi/dns_desec.sh
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#
|
||||||
|
# deSEC.io Domain API
|
||||||
|
#
|
||||||
|
# Author: Zheng Qian
|
||||||
|
#
|
||||||
|
# deSEC API doc
|
||||||
|
# https://desec.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
REST_API="https://desec.io/api/v1/domains"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_desec_add _acme-challenge.foobar.dedyn.io "d41d8cd98f00b204e9800998ecf8427e"
|
||||||
|
dns_desec_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using desec.io api"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
||||||
|
|
||||||
|
if [ -z "$DEDYN_TOKEN" ]; then
|
||||||
|
DEDYN_TOKEN=""
|
||||||
|
_err "You did not specify DEDYN_TOKEN yet."
|
||||||
|
_err "Please create your key and try again."
|
||||||
|
_err "e.g."
|
||||||
|
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the api token to the account conf file.
|
||||||
|
_saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain" "$REST_API/"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
# Get existing TXT record
|
||||||
|
_debug "Getting txt records"
|
||||||
|
txtvalues="\"\\\"$txtvalue\\\"\""
|
||||||
|
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||||
|
|
||||||
|
if [ "$_code" = "200" ]; then
|
||||||
|
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||||
|
_debug "existing TXT found"
|
||||||
|
_debug oldtxtvalues "$oldtxtvalues"
|
||||||
|
if [ -n "$oldtxtvalues" ]; then
|
||||||
|
for oldtxtvalue in $oldtxtvalues; do
|
||||||
|
txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_debug txtvalues "$txtvalues"
|
||||||
|
_info "Adding record"
|
||||||
|
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
|
||||||
|
|
||||||
|
if _desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_desec_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using desec.io api"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
||||||
|
|
||||||
|
if [ -z "$DEDYN_TOKEN" ]; then
|
||||||
|
DEDYN_TOKEN=""
|
||||||
|
_err "You did not specify DEDYN_TOKEN yet."
|
||||||
|
_err "Please create your key and try again."
|
||||||
|
_err "e.g."
|
||||||
|
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain" "$REST_API/"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
# Get existing TXT record
|
||||||
|
_debug "Getting txt records"
|
||||||
|
txtvalues=""
|
||||||
|
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||||
|
|
||||||
|
if [ "$_code" = "200" ]; then
|
||||||
|
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||||
|
_debug "existing TXT found"
|
||||||
|
_debug oldtxtvalues "$oldtxtvalues"
|
||||||
|
if [ -n "$oldtxtvalues" ]; then
|
||||||
|
for oldtxtvalue in $oldtxtvalues; do
|
||||||
|
if [ "$txtvalue" != "$oldtxtvalue" ]; then
|
||||||
|
txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
txtvalues="$(echo "$txtvalues" | cut -c3-)"
|
||||||
|
_debug txtvalues "$txtvalues"
|
||||||
|
|
||||||
|
_info "Deleting record"
|
||||||
|
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
|
||||||
|
_desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"
|
||||||
|
if [ "$_code" = "200" ]; then
|
||||||
|
_info "Deleted, OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "Delete txt record error."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_desec_rest() {
|
||||||
|
m="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
|
||||||
|
export _H1="Authorization: Token $DEDYN_TOKEN"
|
||||||
|
export _H2="Accept: application/json"
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_secure_debug2 data "$data"
|
||||||
|
response="$(_post "$data" "$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ep")"
|
||||||
|
fi
|
||||||
|
_ret="$?"
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
_debug "http response code $_code"
|
||||||
|
_secure_debug2 response "$response"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
response="$(printf "%s" "$response" | _normalizeJson)"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain="$1"
|
||||||
|
ep="$2"
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _desec_rest GET "$ep"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
65
dnsapi/dns_df.sh
Normal file
65
dnsapi/dns_df.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# https://dyndnsfree.de hook script for acme.sh
|
||||||
|
#
|
||||||
|
# Environment variables:
|
||||||
|
#
|
||||||
|
# - $DF_user (your dyndnsfree.de username)
|
||||||
|
# - $DF_password (your dyndnsfree.de password)
|
||||||
|
#
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
dns_df_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txt_value=$2
|
||||||
|
_info "Using DNS-01 dyndnsfree.de hook"
|
||||||
|
|
||||||
|
DF_user="${DF_user:-$(_readaccountconf_mutable DF_user)}"
|
||||||
|
DF_password="${DF_password:-$(_readaccountconf_mutable DF_password)}"
|
||||||
|
if [ -z "$DF_user" ] || [ -z "$DF_password" ]; then
|
||||||
|
DF_user=""
|
||||||
|
DF_password=""
|
||||||
|
_err "No auth details provided. Please set user credentials using the \$DF_user and \$DF_password environment variables."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the api user and password to the account conf file.
|
||||||
|
_debug "Save user and password"
|
||||||
|
_saveaccountconf_mutable DF_user "$DF_user"
|
||||||
|
_saveaccountconf_mutable DF_password "$DF_password"
|
||||||
|
|
||||||
|
domain="$(printf "%s" "$fulldomain" | cut -d"." -f2-)"
|
||||||
|
|
||||||
|
get="$dyndnsfree_api?username=$DF_user&password=$DF_password&hostname=$domain&add_hostname=$fulldomain&txt=$txt_value"
|
||||||
|
|
||||||
|
if ! erg="$(_get "$get")"; then
|
||||||
|
_err "error Adding $fulldomain TXT: $txt_value"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$erg" "success"; then
|
||||||
|
_info "Success, TXT Added, OK"
|
||||||
|
else
|
||||||
|
_err "error Adding $fulldomain TXT: $txt_value erg: $erg"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "ok Auto $fulldomain TXT: $txt_value erg: $erg"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_df_rm() {
|
||||||
|
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "TXT enrty in $fulldomain is deleted automatically"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
}
|
||||||
250
dnsapi/dns_dgon.sh
Executable file
250
dnsapi/dns_dgon.sh
Executable file
@@ -0,0 +1,250 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
## Will be called by acme.sh to add the txt record to your api system.
|
||||||
|
## returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
## Author: thewer <github at thewer.com>
|
||||||
|
## GitHub: https://github.com/gitwer/acme.sh
|
||||||
|
|
||||||
|
##
|
||||||
|
## Environment Variables Required:
|
||||||
|
##
|
||||||
|
## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
|
||||||
|
##
|
||||||
|
|
||||||
|
##################### Public functions #####################
|
||||||
|
|
||||||
|
## Create the text record for validation.
|
||||||
|
## Usage: fulldomain txtvalue
|
||||||
|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
|
||||||
|
dns_dgon_add() {
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
||||||
|
# Check if API Key Exists
|
||||||
|
if [ -z "$DO_API_KEY" ]; then
|
||||||
|
DO_API_KEY=""
|
||||||
|
_err "You did not specify DigitalOcean API key."
|
||||||
|
_err "Please export DO_API_KEY and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Using digitalocean dns validation - add record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
## save the env vars (key and domain split location) for later automated use
|
||||||
|
_saveaccountconf_mutable DO_API_KEY "$DO_API_KEY"
|
||||||
|
|
||||||
|
## split the domain for DO API
|
||||||
|
if ! _get_base_domain "$fulldomain"; then
|
||||||
|
_err "domain not found in your account for addition"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
## Set the header with our post type and key auth key
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Bearer $DO_API_KEY"
|
||||||
|
PURL='https://api.digitalocean.com/v2/domains/'$_domain'/records'
|
||||||
|
PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'","ttl":120}'
|
||||||
|
|
||||||
|
_debug PURL "$PURL"
|
||||||
|
_debug PBODY "$PBODY"
|
||||||
|
|
||||||
|
## the create request - post
|
||||||
|
## args: BODY, URL, [need64, httpmethod]
|
||||||
|
response="$(_post "$PBODY" "$PURL")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
## finished correctly
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## Remove the txt record after validation.
|
||||||
|
## Usage: fulldomain txtvalue
|
||||||
|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
|
||||||
|
dns_dgon_rm() {
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
||||||
|
# Check if API Key Exists
|
||||||
|
if [ -z "$DO_API_KEY" ]; then
|
||||||
|
DO_API_KEY=""
|
||||||
|
_err "You did not specify DigitalOcean API key."
|
||||||
|
_err "Please export DO_API_KEY and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Using digitalocean dns validation - remove record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
## split the domain for DO API
|
||||||
|
if ! _get_base_domain "$fulldomain"; then
|
||||||
|
_err "domain not found in your account for removal"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
## Set the header with our post type and key auth key
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Bearer $DO_API_KEY"
|
||||||
|
## get URL for the list of domains
|
||||||
|
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
|
||||||
|
GURL="https://api.digitalocean.com/v2/domains/$_domain/records"
|
||||||
|
|
||||||
|
## Get all the matching records
|
||||||
|
while true; do
|
||||||
|
## 1) get the URL
|
||||||
|
## the create request - get
|
||||||
|
## args: URL, [onlyheader, timeout]
|
||||||
|
domain_list="$(_get "$GURL")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in domain_list response: $domain_list"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
|
## 2) find records
|
||||||
|
## check for what we are looking for: "type":"A","name":"$_sub_domain"
|
||||||
|
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
|
||||||
|
|
||||||
|
if [ -n "$record" ]; then
|
||||||
|
|
||||||
|
## we found records
|
||||||
|
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
||||||
|
_debug rec_ids "$rec_ids"
|
||||||
|
if [ -n "$rec_ids" ]; then
|
||||||
|
echo "$rec_ids" | while IFS= read -r rec_id; do
|
||||||
|
## delete the record
|
||||||
|
## delete URL for removing the one we dont want
|
||||||
|
DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id"
|
||||||
|
|
||||||
|
## the create request - delete
|
||||||
|
## args: BODY, URL, [need64, httpmethod]
|
||||||
|
response="$(_post "" "$DURL" "" "DELETE")"
|
||||||
|
|
||||||
|
## check response (sort of)
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in remove response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
## 3) find the next page
|
||||||
|
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
|
||||||
|
if [ -z "$nextpage" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
_debug2 nextpage "$nextpage"
|
||||||
|
GURL="$nextpage"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
## finished correctly
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
##################### Private functions below #####################
|
||||||
|
|
||||||
|
## Split the domain provided into the "bade domain" and the "start prefix".
|
||||||
|
## This function searches for the longest subdomain in your account
|
||||||
|
## for the full domain given and splits it into the base domain (zone)
|
||||||
|
## and the prefix/record to be added/removed
|
||||||
|
## USAGE: fulldomain
|
||||||
|
## EG: "_acme-challenge.two.three.four.domain.com"
|
||||||
|
## returns
|
||||||
|
## _sub_domain="_acme-challenge.two"
|
||||||
|
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
|
||||||
|
## if only "domain.com" exists it will return
|
||||||
|
## _sub_domain="_acme-challenge.two.three.four"
|
||||||
|
## _domain="domain.com"
|
||||||
|
_get_base_domain() {
|
||||||
|
# args
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
|
||||||
|
# domain max legal length = 253
|
||||||
|
MAX_DOM=255
|
||||||
|
|
||||||
|
## get a list of domains for the account to check thru
|
||||||
|
## Set the headers
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Bearer $DO_API_KEY"
|
||||||
|
_debug DO_API_KEY "$DO_API_KEY"
|
||||||
|
## get URL for the list of domains
|
||||||
|
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
|
||||||
|
DOMURL="https://api.digitalocean.com/v2/domains"
|
||||||
|
|
||||||
|
## while we dont have a matching domain we keep going
|
||||||
|
while [ -z "$found" ]; do
|
||||||
|
## get the domain list (current page)
|
||||||
|
domain_list="$(_get "$DOMURL")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in domain_list response: $domain_list"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
|
## for each shortening of our $fulldomain, check if it exists in the $domain_list
|
||||||
|
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
|
||||||
|
i=2
|
||||||
|
while [ $i -gt 0 ]; do
|
||||||
|
## get next longest domain
|
||||||
|
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
||||||
|
## check we got something back from our cut (or are we at the end)
|
||||||
|
if [ -z "$_domain" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
## we got part of a domain back - grep it out
|
||||||
|
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
|
||||||
|
## check if it exists
|
||||||
|
if [ -n "$found" ]; then
|
||||||
|
## exists - exit loop returning the parts
|
||||||
|
sub_point=$(_math $i - 1)
|
||||||
|
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
## increment cut point $i
|
||||||
|
i=$(_math $i + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$found" ]; then
|
||||||
|
## find the next page if we dont have a match
|
||||||
|
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
|
||||||
|
if [ -z "$nextpage" ]; then
|
||||||
|
_err "no record and no nextpage in digital ocean DNS removal"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 nextpage "$nextpage"
|
||||||
|
DOMURL="$nextpage"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
## we went through the entire domain zone list and dint find one that matched
|
||||||
|
## doesnt look like we can add in the record
|
||||||
|
_err "domain not found in DigitalOcean account, but we should never get here"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
87
dnsapi/dns_dnshome.sh
Executable file
87
dnsapi/dns_dnshome.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# dnsHome.de API for acme.sh
|
||||||
|
#
|
||||||
|
# This Script adds the necessary TXT record to a Subdomain
|
||||||
|
#
|
||||||
|
# Author dnsHome.de (https://github.com/dnsHome-de)
|
||||||
|
#
|
||||||
|
# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819
|
||||||
|
#
|
||||||
|
# export DNSHOME_Subdomain=""
|
||||||
|
# export DNSHOME_SubdomainPassword=""
|
||||||
|
|
||||||
|
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_dnshome_add() {
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
|
||||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
|
||||||
|
|
||||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
|
||||||
|
DNSHOME_Subdomain=""
|
||||||
|
DNSHOME_SubdomainPassword=""
|
||||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_savedomainconf DNSHOME_Subdomain "$DNSHOME_Subdomain"
|
||||||
|
_savedomainconf DNSHOME_SubdomainPassword "$DNSHOME_SubdomainPassword"
|
||||||
|
|
||||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
|
||||||
|
|
||||||
|
_DNSHOME_rest POST "acme=add&txt=$txtvalue"
|
||||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_dnshome_rm() {
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
|
||||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
|
||||||
|
|
||||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
|
||||||
|
|
||||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
|
||||||
|
DNSHOME_Subdomain=""
|
||||||
|
DNSHOME_SubdomainPassword=""
|
||||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_DNSHOME_rest POST "acme=rm&txt=$txtvalue"
|
||||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_DNSHOME_rest() {
|
||||||
|
method=$1
|
||||||
|
data="$2"
|
||||||
|
_debug "$data"
|
||||||
|
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$DNSHOME_Api" "" "$method")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $data"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
198
dnsapi/dns_dnsimple.sh
Normal file
198
dnsapi/dns_dnsimple.sh
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# DNSimple domain api
|
||||||
|
# https://github.com/pho3nixf1re/acme.sh/issues
|
||||||
|
#
|
||||||
|
# This is your oauth token which can be acquired on the account page. Please
|
||||||
|
# note that this must be an _account_ token and not a _user_ token.
|
||||||
|
# https://dnsimple.com/a/<your account id>/account/access_tokens
|
||||||
|
# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
|
||||||
|
DNSimple_API="https://api.dnsimple.com/v2"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dnsimple_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$DNSimple_OAUTH_TOKEN" ]; then
|
||||||
|
DNSimple_OAUTH_TOKEN=""
|
||||||
|
_err "You have not set the dnsimple oauth token yet."
|
||||||
|
_err "Please visit https://dnsimple.com/user to generate it."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# save the oauth token for later
|
||||||
|
_saveaccountconf DNSimple_OAUTH_TOKEN "$DNSimple_OAUTH_TOKEN"
|
||||||
|
|
||||||
|
if ! _get_account_id; then
|
||||||
|
_err "failed to retrive account id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_get_records "$_account_id" "$_domain" "$_sub_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then
|
||||||
|
_info "Added"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Unexpected response while adding text record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
}
|
||||||
|
|
||||||
|
# fulldomain
|
||||||
|
dns_dnsimple_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
|
||||||
|
if ! _get_account_id; then
|
||||||
|
_err "failed to retrive account id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_get_records "$_account_id" "$_domain" "$_sub_domain"
|
||||||
|
|
||||||
|
_extract_record_id "$_records" "$_sub_domain"
|
||||||
|
if [ "$_record_id" ]; then
|
||||||
|
echo "$_record_id" | while read -r item; do
|
||||||
|
if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$item"; then
|
||||||
|
_info "removed record" "$item"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "failed to remove record" "$item"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions bellow ##################################
|
||||||
|
# _acme-challenge.www.domain.com
|
||||||
|
# returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
previous=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
# not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dnsimple_rest GET "$_account_id/zones/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" 'not found'; then
|
||||||
|
_debug "$h not found"
|
||||||
|
else
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
|
||||||
|
_domain="$h"
|
||||||
|
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
previous="$i"
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns _account_id
|
||||||
|
_get_account_id() {
|
||||||
|
_debug "retrive account id"
|
||||||
|
if ! _dnsimple_rest GET "whoami"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"account\":null"; then
|
||||||
|
_err "no account associated with this token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "timeout"; then
|
||||||
|
_err "timeout retrieving account id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_account_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"email\":" | cut -d: -f2 | cut -d, -f1)
|
||||||
|
_debug _account_id "$_account_id"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns
|
||||||
|
# _records
|
||||||
|
# _records_count
|
||||||
|
_get_records() {
|
||||||
|
account_id=$1
|
||||||
|
domain=$2
|
||||||
|
sub_domain=$3
|
||||||
|
|
||||||
|
_debug "fetching txt records"
|
||||||
|
_dnsimple_rest GET "$account_id/zones/$domain/records?per_page=5000&sort=id:desc"
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"id\":"; then
|
||||||
|
_err "failed to retrieve records"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_records_count=$(printf "%s" "$response" | _egrep_o "\"name\":\"$sub_domain\"" | wc -l | _egrep_o "[0-9]+")
|
||||||
|
_records=$response
|
||||||
|
_debug _records_count "$_records_count"
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns _record_id
|
||||||
|
_extract_record_id() {
|
||||||
|
_record_id=$(printf "%s" "$_records" | _egrep_o "\"id\":[^,]*,\"zone_id\":\"[^,]*\",\"parent_id\":null,\"name\":\"$_sub_domain\"" | cut -d: -f2 | cut -d, -f1)
|
||||||
|
_debug "_record_id" "$_record_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns response
|
||||||
|
_dnsimple_rest() {
|
||||||
|
method=$1
|
||||||
|
path="$2"
|
||||||
|
data="$3"
|
||||||
|
request_url="$DNSimple_API/$path"
|
||||||
|
_debug "$path"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="Authorization: Bearer $DNSimple_OAUTH_TOKEN"
|
||||||
|
|
||||||
|
if [ "$data" ] || [ "$method" = "DELETE" ]; then
|
||||||
|
_H1="Content-Type: application/json"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$request_url" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$request_url" "" "" "$method")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $request_url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
148
dnsapi/dns_do.sh
Executable file
148
dnsapi/dns_do.sh
Executable file
@@ -0,0 +1,148 @@
|
|||||||
|
#!/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'
|
||||||
|
}
|
||||||
59
dnsapi/dns_doapi.sh
Executable file
59
dnsapi/dns_doapi.sh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Official Let's Encrypt API for do.de / Domain-Offensive
|
||||||
|
#
|
||||||
|
# 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 #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_doapi_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
|
||||||
|
if [ -z "$DO_LETOKEN" ]; then
|
||||||
|
DO_LETOKEN=""
|
||||||
|
_err "You didn't configure a do.de API token yet."
|
||||||
|
_err "Please set DO_LETOKEN and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
|
||||||
|
|
||||||
|
_info "Adding TXT record to ${fulldomain}"
|
||||||
|
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")"
|
||||||
|
if _contains "${response}" 'success'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err "Could not create resource record, check logs"
|
||||||
|
_err "${response}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_doapi_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
|
||||||
|
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
|
||||||
|
if [ -z "$DO_LETOKEN" ]; then
|
||||||
|
DO_LETOKEN=""
|
||||||
|
_err "You didn't configure a do.de API token yet."
|
||||||
|
_err "Please set DO_LETOKEN and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
|
||||||
|
|
||||||
|
_info "Deleting resource record $fulldomain"
|
||||||
|
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&action=delete")"
|
||||||
|
if _contains "${response}" 'success'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err "Could not delete resource record, check logs"
|
||||||
|
_err "${response}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
155
dnsapi/dns_domeneshop.sh
Normal file
155
dnsapi/dns_domeneshop.sh
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
|
||||||
|
|
||||||
|
##################### Public functions #####################
|
||||||
|
|
||||||
|
# Usage: dns_domeneshop_add <full domain> <txt record>
|
||||||
|
# Example: dns_domeneshop_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_domeneshop_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
# Get token and secret
|
||||||
|
DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}"
|
||||||
|
DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then
|
||||||
|
DOMENESHOP_Token=""
|
||||||
|
DOMENESHOP_Secret=""
|
||||||
|
_err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save the api token and secret.
|
||||||
|
_saveaccountconf_mutable DOMENESHOP_Token "$DOMENESHOP_Token"
|
||||||
|
_saveaccountconf_mutable DOMENESHOP_Secret "$DOMENESHOP_Secret"
|
||||||
|
|
||||||
|
# Get the domain name id
|
||||||
|
if ! _get_domainid "$fulldomain"; then
|
||||||
|
_err "Did not find domainname"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create record
|
||||||
|
_domeneshop_rest POST "domains/$_domainid/dns" "{\"type\":\"TXT\",\"host\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"ttl\":120}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: dns_domeneshop_rm <full domain> <txt record>
|
||||||
|
# Example: dns_domeneshop_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_domeneshop_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
# Get token and secret
|
||||||
|
DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}"
|
||||||
|
DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then
|
||||||
|
DOMENESHOP_Token=""
|
||||||
|
DOMENESHOP_Secret=""
|
||||||
|
_err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the domain name id
|
||||||
|
if ! _get_domainid "$fulldomain"; then
|
||||||
|
_err "Did not find domainname"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find record
|
||||||
|
if ! _get_recordid "$_domainid" "$_sub_domain" "$txtvalue"; then
|
||||||
|
_err "Did not find dns record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove record
|
||||||
|
_domeneshop_rest DELETE "domains/$_domainid/dns/$_recordid"
|
||||||
|
}
|
||||||
|
|
||||||
|
##################### Private functions #####################
|
||||||
|
|
||||||
|
_get_domainid() {
|
||||||
|
domain=$1
|
||||||
|
|
||||||
|
# Get domains
|
||||||
|
_domeneshop_rest GET "domains"
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"id\":"; then
|
||||||
|
_err "failed to get domain names"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug "h" "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
|
# We have found the domain name.
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_recordid() {
|
||||||
|
domainid=$1
|
||||||
|
subdomain=$2
|
||||||
|
txtvalue=$3
|
||||||
|
|
||||||
|
# Get all dns records for the domainname
|
||||||
|
_domeneshop_rest GET "domains/$domainid/dns"
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"id\":"; then
|
||||||
|
_debug "No records in dns"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "\"host\":\"$subdomain\""; then
|
||||||
|
_debug "Record does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the id of the record in question
|
||||||
|
_recordid=$(printf "%s" "$response" | _egrep_o "[^{]*\"host\":\"$subdomain\"[^}]*" | _egrep_o "[^{]*\"data\":\"$txtvalue\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
||||||
|
if [ -z "$_recordid" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_domeneshop_rest() {
|
||||||
|
method=$1
|
||||||
|
endpoint=$2
|
||||||
|
data=$3
|
||||||
|
|
||||||
|
credentials=$(printf "%b" "$DOMENESHOP_Token:$DOMENESHOP_Secret" | _base64)
|
||||||
|
|
||||||
|
export _H1="Authorization: Basic $credentials"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$method" != "GET" ]; then
|
||||||
|
response="$(_post "$data" "$DOMENESHOP_Api_Endpoint/$endpoint" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$DOMENESHOP_Api_Endpoint/$endpoint")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $endpoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
163
dnsapi/dns_dp.sh
163
dnsapi/dns_dp.sh
@@ -6,11 +6,8 @@
|
|||||||
#
|
#
|
||||||
#DP_Key="sADDsdasdgdsf"
|
#DP_Key="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
REST_API="https://dnsapi.cn"
|
||||||
|
|
||||||
DP_Api="https://dnsapi.cn"
|
|
||||||
|
|
||||||
|
|
||||||
#REST_API
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@@ -18,68 +15,68 @@ dns_dp_add() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
|
DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
|
||||||
|
DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
|
||||||
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
|
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
|
||||||
|
DP_Id=""
|
||||||
|
DP_Key=""
|
||||||
_err "You don't specify dnspod api key and key id yet."
|
_err "You don't specify dnspod api key and key id yet."
|
||||||
_err "Please create you key and try again."
|
_err "Please create you key and try again."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REST_API=$DP_Api
|
|
||||||
|
|
||||||
#save the api key and email to the account conf file.
|
#save the api key and email to the account conf file.
|
||||||
_saveaccountconf DP_Id "$DP_Id"
|
_saveaccountconf_mutable DP_Id "$DP_Id"
|
||||||
_saveaccountconf DP_Key "$DP_Key"
|
_saveaccountconf_mutable DP_Key "$DP_Key"
|
||||||
|
|
||||||
|
|
||||||
_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"
|
_err "invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
existing_records $_domain $_sub_domain
|
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
_debug count "$count"
|
|
||||||
if [ "$?" != "0" ] ; then
|
|
||||||
_err "Error get existing records."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$count" = "0" ] ; then
|
|
||||||
add_record $_domain $_sub_domain $txtvalue
|
|
||||||
else
|
|
||||||
update_record $_domain $_sub_domain $txtvalue
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#usage: root sub
|
#fulldomain txtvalue
|
||||||
#return if the sub record already exists.
|
dns_dp_rm() {
|
||||||
#echos the existing records count.
|
fulldomain=$1
|
||||||
# '0' means doesn't exist
|
txtvalue=$2
|
||||||
existing_records() {
|
|
||||||
_debug "Getting txt records"
|
|
||||||
root=$1
|
|
||||||
sub=$2
|
|
||||||
|
|
||||||
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
|
||||||
|
DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if printf "$response" | grep 'No records' ; then
|
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||||
count=0;
|
_err "Record.Lis error."
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf "$response" | grep "Action completed successful" >/dev/null ; then
|
|
||||||
count=$(printf "$response" | grep '<type>TXT</type>' | wc -l)
|
|
||||||
record_id=$(printf "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
_err "get existing records error."
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" 'No records'; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&record_id=$record_id"; then
|
||||||
|
_err "Record.Remove error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_contains "$response" "successful"
|
||||||
|
|
||||||
count=0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#add the txt record.
|
#add the txt record.
|
||||||
@@ -88,49 +85,18 @@ add_record() {
|
|||||||
root=$1
|
root=$1
|
||||||
sub=$2
|
sub=$2
|
||||||
txtvalue=$3
|
txtvalue=$3
|
||||||
fulldomain=$sub.$root
|
fulldomain="$sub.$root"
|
||||||
|
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
|
|
||||||
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
|
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=%E9%BB%98%E8%AE%A4"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if printf "$response" | grep "Action completed successful" ; then
|
_contains "$response" "successful" || _contains "$response" "Domain record already exists"
|
||||||
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
return 1 #error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#update the txt record
|
#################### Private functions below ##################################
|
||||||
#Usage: root sub txtvalue
|
|
||||||
update_record() {
|
|
||||||
root=$1
|
|
||||||
sub=$2
|
|
||||||
txtvalue=$3
|
|
||||||
fulldomain=$sub.$root
|
|
||||||
|
|
||||||
_info "Updating record"
|
|
||||||
|
|
||||||
if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf "$response" | grep "Action completed successful" ; then
|
|
||||||
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1 #error
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################### Private functions bellow ##################################
|
|
||||||
#_acme-challenge.www.domain.com
|
#_acme-challenge.www.domain.com
|
||||||
#returns
|
#returns
|
||||||
# _sub_domain=_acme-challenge.www
|
# _sub_domain=_acme-challenge.www
|
||||||
@@ -140,51 +106,50 @@ _get_root() {
|
|||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while [ '1' ] ; do
|
while true; do
|
||||||
h=$(printf $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;
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if printf "$response" | grep "Action completed successful" >/dev/null ; then
|
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain=$h"; then
|
||||||
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "successful"; then
|
||||||
|
_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 $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"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
p=$i
|
p="$i"
|
||||||
i=$(expr $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#Usage: method URI data
|
#Usage: method URI data
|
||||||
_rest() {
|
_rest() {
|
||||||
m=$1
|
m="$1"
|
||||||
ep="$2"
|
ep="$2"
|
||||||
data="$3"
|
data="$3"
|
||||||
_debug $ep
|
_debug "$ep"
|
||||||
url="$REST_API/$ep"
|
url="$REST_API/$ep"
|
||||||
|
|
||||||
_debug url "$url"
|
_debug url "$url"
|
||||||
|
|
||||||
if [ "$data" ] ; then
|
if [ "$m" = "GET" ]; then
|
||||||
_debug2 data "$data"
|
response="$(_get "$url" | tr -d '\r')"
|
||||||
response="$(_post $data "$url")"
|
|
||||||
else
|
else
|
||||||
response="$(_get "$url")"
|
_debug2 data "$data"
|
||||||
|
response="$(_post "$data" "$url" | tr -d '\r')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
@@ -194,5 +159,3 @@ _rest() {
|
|||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
161
dnsapi/dns_dpi.sh
Executable file
161
dnsapi/dns_dpi.sh
Executable file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Dnspod.com Domain api
|
||||||
|
#
|
||||||
|
#DPI_Id="1234"
|
||||||
|
#
|
||||||
|
#DPI_Key="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
REST_API="https://api.dnspod.com"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dpi_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}"
|
||||||
|
DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}"
|
||||||
|
if [ -z "$DPI_Id" ] || [ -z "$DPI_Key" ]; then
|
||||||
|
DPI_Id=""
|
||||||
|
DPI_Key=""
|
||||||
|
_err "You don't specify dnspod api key and key id yet."
|
||||||
|
_err "Please create you key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable DPI_Id "$DPI_Id"
|
||||||
|
_saveaccountconf_mutable DPI_Key "$DPI_Key"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_dpi_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}"
|
||||||
|
DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _rest POST "Record.List" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||||
|
_err "Record.Lis error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" 'No records'; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _rest POST "Record.Remove" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
|
||||||
|
_err "Record.Remove error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_contains "$response" "Operation successful"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#add the txt record.
|
||||||
|
#usage: root sub txtvalue
|
||||||
|
add_record() {
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
txtvalue=$3
|
||||||
|
fulldomain="$sub.$root"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
|
||||||
|
if ! _rest POST "Record.Create" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_contains "$response" "Operation successful" || _contains "$response" "Domain record already exists"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _rest POST "Domain.Info" "login_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "Operation successful"; then
|
||||||
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_domain="$h"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p="$i"
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: method URI data
|
||||||
|
_rest() {
|
||||||
|
m="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
url="$REST_API/$ep"
|
||||||
|
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
if [ "$m" = "GET" ]; then
|
||||||
|
response="$(_get "$url" | tr -d '\r')"
|
||||||
|
else
|
||||||
|
_debug2 data "$data"
|
||||||
|
response="$(_post "$data" "$url" | tr -d '\r')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
97
dnsapi/dns_dreamhost.sh
Normal file
97
dnsapi/dns_dreamhost.sh
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Author: RhinoLance
|
||||||
|
#Report Bugs here: https://github.com/RhinoLance/acme.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
#define the api endpoint
|
||||||
|
DH_API_ENDPOINT="https://api.dreamhost.com/"
|
||||||
|
querystring=""
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dreamhost_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! validate "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
querystring="key=$DH_API_KEY&cmd=dns-add_record&record=$fulldomain&type=TXT&value=$txtvalue"
|
||||||
|
if ! submit "$querystring"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_dreamhost_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! validate "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
querystring="key=$DH_API_KEY&cmd=dns-remove_record&record=$fulldomain&type=TXT&value=$txtvalue"
|
||||||
|
if ! submit "$querystring"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
#send the command to the api endpoint.
|
||||||
|
submit() {
|
||||||
|
querystring=$1
|
||||||
|
|
||||||
|
url="$DH_API_ENDPOINT?$querystring"
|
||||||
|
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
if ! response="$(_get "$url")"; then
|
||||||
|
_err "Error <$1>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||||
|
if [ -n "$message" ]; then
|
||||||
|
_err "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#check that we have a valid API Key
|
||||||
|
validate() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using dreamhost"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
#retrieve the API key from the environment variable if it exists, otherwise look for a saved key.
|
||||||
|
DH_API_KEY="${DH_API_KEY:-$(_readaccountconf_mutable DH_API_KEY)}"
|
||||||
|
|
||||||
|
if [ -z "$DH_API_KEY" ]; then
|
||||||
|
DH_API_KEY=""
|
||||||
|
_err "You didn't specify the DreamHost api key yet (export DH_API_KEY=\"<api key>\")"
|
||||||
|
_err "Please login to your control panel, create a key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key to the account conf file.
|
||||||
|
_saveaccountconf_mutable DH_API_KEY "$DH_API_KEY"
|
||||||
|
}
|
||||||
132
dnsapi/dns_duckdns.sh
Executable file
132
dnsapi/dns_duckdns.sh
Executable file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
||||||
|
#06/27/2017
|
||||||
|
|
||||||
|
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
|
||||||
|
# --
|
||||||
|
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
|
# --
|
||||||
|
#
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
######## Public functions ######################
|
||||||
|
|
||||||
|
#Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_duckdns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
|
||||||
|
if [ -z "$DuckDNS_Token" ]; then
|
||||||
|
_err "You must export variable: DuckDNS_Token"
|
||||||
|
_err "The token for your DuckDNS account is necessary."
|
||||||
|
_err "You can look it up in your DuckDNS account."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now save the credentials.
|
||||||
|
_saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token"
|
||||||
|
|
||||||
|
# Unfortunately, DuckDNS does not seems to support lookup domain through API
|
||||||
|
# So I assume your credentials (which are your domain and token) are correct
|
||||||
|
# If something goes wrong, we will get a KO response from DuckDNS
|
||||||
|
|
||||||
|
if ! _duckdns_get_domain; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now add the TXT record to DuckDNS
|
||||||
|
_info "Trying to add TXT record"
|
||||||
|
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then
|
||||||
|
if [ "$response" = "OK" ]; then
|
||||||
|
_info "TXT record has been successfully added to your DuckDNS domain."
|
||||||
|
_info "Note that all subdomains under this domain uses the same TXT record."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Errors happened during adding the TXT record, response=$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Errors happened during adding the TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_duckdns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
|
||||||
|
if [ -z "$DuckDNS_Token" ]; then
|
||||||
|
_err "You must export variable: DuckDNS_Token"
|
||||||
|
_err "The token for your DuckDNS account is necessary."
|
||||||
|
_err "You can look it up in your DuckDNS account."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _duckdns_get_domain; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now remove the TXT record from DuckDNS
|
||||||
|
_info "Trying to remove TXT record"
|
||||||
|
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then
|
||||||
|
if [ "$response" = "OK" ]; then
|
||||||
|
_info "TXT record has been successfully removed from your DuckDNS domain."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Errors happened during removing the TXT record, response=$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Errors happened during removing the TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# fulldomain may be 'domain.duckdns.org' (if using --domain-alias) or '_acme-challenge.domain.duckdns.org'
|
||||||
|
# either way, return 'domain'. (duckdns does not allow further subdomains and restricts domains to [a-z0-9-].)
|
||||||
|
_duckdns_get_domain() {
|
||||||
|
|
||||||
|
# We'll extract the domain/username from full domain
|
||||||
|
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '^(_acme-challenge\.)?([a-z0-9-]+\.)+duckdns\.org' | sed -n 's/^\([^.]\{1,\}\.\)*\([a-z0-9-]\{1,\}\)\.duckdns\.org$/\2/p;')"
|
||||||
|
|
||||||
|
if [ -z "$_duckdns_domain" ]; then
|
||||||
|
_err "Error extracting the domain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: method URI
|
||||||
|
_duckdns_rest() {
|
||||||
|
method=$1
|
||||||
|
param="$2"
|
||||||
|
_debug param "$param"
|
||||||
|
url="$DuckDNS_API?$param"
|
||||||
|
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ]; then
|
||||||
|
url="$url&verbose=true"
|
||||||
|
fi
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
# DuckDNS uses GET to update domain info
|
||||||
|
if [ "$method" = "GET" ]; then
|
||||||
|
response="$(_get "$url")"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then
|
||||||
|
response="OK"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Unsupported method"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
176
dnsapi/dns_durabledns.sh
Normal file
176
dnsapi/dns_durabledns.sh
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#DD_API_User="xxxxx"
|
||||||
|
#DD_API_Key="xxxxxx"
|
||||||
|
|
||||||
|
_DD_BASE="https://durabledns.com/services/dns"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_durabledns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DD_API_User="${DD_API_User:-$(_readaccountconf_mutable DD_API_User)}"
|
||||||
|
DD_API_Key="${DD_API_Key:-$(_readaccountconf_mutable DD_API_Key)}"
|
||||||
|
if [ -z "$DD_API_User" ] || [ -z "$DD_API_Key" ]; then
|
||||||
|
DD_API_User=""
|
||||||
|
DD_API_Key=""
|
||||||
|
_err "You didn't specify a durabledns api user or key yet."
|
||||||
|
_err "You can get yours from here https://durabledns.com/dashboard/index.php"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable DD_API_User "$DD_API_User"
|
||||||
|
_saveaccountconf_mutable DD_API_Key "$DD_API_Key"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
_dd_soap createRecord string zonename "$_domain." string name "$_sub_domain" string type "TXT" string data "$txtvalue" int aux 0 int ttl 10 string ddns_enabled N
|
||||||
|
_contains "$response" "createRecordResponse"
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_durabledns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DD_API_User="${DD_API_User:-$(_readaccountconf_mutable DD_API_User)}"
|
||||||
|
DD_API_Key="${DD_API_Key:-$(_readaccountconf_mutable DD_API_Key)}"
|
||||||
|
if [ -z "$DD_API_User" ] || [ -z "$DD_API_Key" ]; then
|
||||||
|
DD_API_User=""
|
||||||
|
DD_API_Key=""
|
||||||
|
_err "You didn't specify a durabledns api user or key yet."
|
||||||
|
_err "You can get yours from here https://durabledns.com/dashboard/index.php"
|
||||||
|
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"
|
||||||
|
|
||||||
|
_debug "Find record id"
|
||||||
|
if ! _dd_soap listRecords string zonename "$_domain."; then
|
||||||
|
_err "can not listRecords"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
subtxt="$(echo "$txtvalue" | cut -c 1-30)"
|
||||||
|
record="$(echo "$response" | sed 's/<item\>/#<item>/g' | tr '#' '\n' | grep ">$subtxt")"
|
||||||
|
_debug record "$record"
|
||||||
|
if [ -z "$record" ]; then
|
||||||
|
_err "can not find record for txtvalue" "$txtvalue"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
recordid="$(echo "$record" | _egrep_o '<id xsi:type="xsd:int">[0-9]*</id>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug recordid "$recordid"
|
||||||
|
if [ -z "$recordid" ]; then
|
||||||
|
_err "can not find record id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dd_soap deleteRecord string zonename "$_domain." int id "$recordid"; then
|
||||||
|
_err "delete error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_contains "$response" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
if ! _dd_soap "listZones"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" ">$h.</origin>"; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#method
|
||||||
|
_dd_soap() {
|
||||||
|
_method="$1"
|
||||||
|
shift
|
||||||
|
_urn="${_method}wsdl"
|
||||||
|
# put the parameters to xml
|
||||||
|
body="<tns:$_method>
|
||||||
|
<apiuser xsi:type=\"xsd:string\">$DD_API_User</apiuser>
|
||||||
|
<apikey xsi:type=\"xsd:string\">$DD_API_Key</apikey>
|
||||||
|
"
|
||||||
|
while [ "$1" ]; do
|
||||||
|
_t="$1"
|
||||||
|
shift
|
||||||
|
_k="$1"
|
||||||
|
shift
|
||||||
|
_v="$1"
|
||||||
|
shift
|
||||||
|
body="$body<$_k xsi:type=\"xsd:$_t\">$_v</$_k>"
|
||||||
|
done
|
||||||
|
body="$body</tns:$_method>"
|
||||||
|
_debug2 "SOAP request ${body}"
|
||||||
|
|
||||||
|
# build SOAP XML
|
||||||
|
_xml='<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
|
||||||
|
xmlns:tns="urn:'$_urn'"
|
||||||
|
xmlns:types="urn:'$_urn'/encodedTypes"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'"$body"'</soap:Body>
|
||||||
|
</soap:Envelope>'
|
||||||
|
|
||||||
|
_debug2 _xml "$_xml"
|
||||||
|
# set SOAP headers
|
||||||
|
_action="SOAPAction: \"urn:$_urn#$_method\""
|
||||||
|
_debug2 "_action" "$_action"
|
||||||
|
export _H1="$_action"
|
||||||
|
export _H2="Content-Type: text/xml; charset=utf-8"
|
||||||
|
|
||||||
|
_url="$_DD_BASE/$_method.php"
|
||||||
|
_debug "_url" "$_url"
|
||||||
|
if ! response="$(_post "${_xml}" "${_url}")"; then
|
||||||
|
_err "Error <$1>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 "response" "$response"
|
||||||
|
response="$(echo "$response" | tr -d "\r\n" | _egrep_o ":${_method}Response .*:${_method}Response><")"
|
||||||
|
_debug2 "response" "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
339
dnsapi/dns_dyn.sh
Normal file
339
dnsapi/dns_dyn.sh
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#
|
||||||
|
# Dyn.com Domain API
|
||||||
|
#
|
||||||
|
# Author: Gerd Naschenweng
|
||||||
|
# https://github.com/magicdude4eva
|
||||||
|
#
|
||||||
|
# Dyn Managed DNS API
|
||||||
|
# https://help.dyn.com/dns-api-knowledge-base/
|
||||||
|
#
|
||||||
|
# It is recommended to add a "Dyn Managed DNS" user specific for API access.
|
||||||
|
# The "Zones & Records Permissions" required by this script are:
|
||||||
|
# --
|
||||||
|
# RecordAdd
|
||||||
|
# RecordUpdate
|
||||||
|
# RecordDelete
|
||||||
|
# RecordGet
|
||||||
|
# ZoneGet
|
||||||
|
# ZoneAddNode
|
||||||
|
# ZoneRemoveNode
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
#REST_API
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "Challenge-code"
|
||||||
|
dns_dyn_add() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}"
|
||||||
|
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}"
|
||||||
|
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}"
|
||||||
|
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then
|
||||||
|
DYN_Customer=""
|
||||||
|
DYN_Username=""
|
||||||
|
DYN_Password=""
|
||||||
|
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the config variables to the account conf file.
|
||||||
|
_saveaccountconf_mutable DYN_Customer "$DYN_Customer"
|
||||||
|
_saveaccountconf_mutable DYN_Username "$DYN_Username"
|
||||||
|
_saveaccountconf_mutable DYN_Password "$DYN_Password"
|
||||||
|
|
||||||
|
if ! _dyn_get_authtoken; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_dyn_authtoken" ]; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_get_zone; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_add_record; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_publish_zone; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dyn_end_session
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_dyn_rm() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}"
|
||||||
|
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}"
|
||||||
|
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}"
|
||||||
|
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then
|
||||||
|
DYN_Customer=""
|
||||||
|
DYN_Username=""
|
||||||
|
DYN_Password=""
|
||||||
|
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_get_authtoken; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_dyn_authtoken" ]; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_get_zone; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_get_record_id; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_dyn_record_id" ]; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_rm_record; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dyn_publish_zone; then
|
||||||
|
_dyn_end_session
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dyn_end_session
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
#get Auth-Token
|
||||||
|
_dyn_get_authtoken() {
|
||||||
|
|
||||||
|
_info "Start Dyn API Session"
|
||||||
|
|
||||||
|
data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}"
|
||||||
|
dyn_url="$DYN_API/Session/"
|
||||||
|
method="POST"
|
||||||
|
|
||||||
|
_debug data "$data"
|
||||||
|
_debug dyn_url "$dyn_url"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_post "$data" "$dyn_url" "" "$method")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')"
|
||||||
|
_info "Token received"
|
||||||
|
_debug _dyn_authtoken "$_dyn_authtoken"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dyn_authtoken=""
|
||||||
|
_err "get token failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain=_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _dyn_zone=domain.com
|
||||||
|
_dyn_get_zone() {
|
||||||
|
i=2
|
||||||
|
while true; do
|
||||||
|
domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")"
|
||||||
|
if [ -z "$domain" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
dyn_url="$DYN_API/Zone/$domain/"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_get "$dyn_url" "" "")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug dyn_url "$dyn_url"
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_dyn_zone="$domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
_dyn_zone=""
|
||||||
|
_err "get zone failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#add TXT record
|
||||||
|
_dyn_add_record() {
|
||||||
|
|
||||||
|
_info "Adding TXT record"
|
||||||
|
|
||||||
|
data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}"
|
||||||
|
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/"
|
||||||
|
method="POST"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_post "$data" "$dyn_url" "" "$method")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_info "TXT Record successfully added"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "add TXT record failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#publish the zone
|
||||||
|
_dyn_publish_zone() {
|
||||||
|
|
||||||
|
_info "Publishing zone"
|
||||||
|
|
||||||
|
data="{\"publish\":\"true\"}"
|
||||||
|
dyn_url="$DYN_API/Zone/$_dyn_zone/"
|
||||||
|
method="PUT"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_post "$data" "$dyn_url" "" "$method")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_info "Zone published"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "publish zone failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#get record_id of TXT record so we can delete the record
|
||||||
|
_dyn_get_record_id() {
|
||||||
|
|
||||||
|
_info "Getting record_id of TXT record"
|
||||||
|
|
||||||
|
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_get "$dyn_url" "" "")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")"
|
||||||
|
_debug _dyn_record_id "$_dyn_record_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dyn_record_id=""
|
||||||
|
_err "getting record_id failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#delete TXT record
|
||||||
|
_dyn_rm_record() {
|
||||||
|
|
||||||
|
_info "Deleting TXT record"
|
||||||
|
|
||||||
|
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/"
|
||||||
|
method="DELETE"
|
||||||
|
|
||||||
|
_debug dyn_url "$dyn_url"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_post "" "$dyn_url" "" "$method")"
|
||||||
|
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
_debug sessionstatus "$sessionstatus"
|
||||||
|
|
||||||
|
if [ "$sessionstatus" = "success" ]; then
|
||||||
|
_info "TXT record successfully deleted"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err "delete TXT record failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#logout
|
||||||
|
_dyn_end_session() {
|
||||||
|
|
||||||
|
_info "End Dyn API Session"
|
||||||
|
|
||||||
|
dyn_url="$DYN_API/Session/"
|
||||||
|
method="DELETE"
|
||||||
|
|
||||||
|
_debug dyn_url "$dyn_url"
|
||||||
|
|
||||||
|
export _H1="Auth-Token: $_dyn_authtoken"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_post "" "$dyn_url" "" "$method")"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
_dyn_authtoken=""
|
||||||
|
return 0
|
||||||
|
}
|
||||||
232
dnsapi/dns_dynu.sh
Normal file
232
dnsapi/dns_dynu.sh
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Client ID
|
||||||
|
#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
|
||||||
|
#
|
||||||
|
#Secret
|
||||||
|
#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
|
||||||
|
#
|
||||||
|
#Token
|
||||||
|
Dynu_Token=""
|
||||||
|
#
|
||||||
|
#Endpoint
|
||||||
|
Dynu_EndPoint="https://api.dynu.com/v2"
|
||||||
|
#
|
||||||
|
#Author: Dynu Systems, Inc.
|
||||||
|
#Report Bugs here: https://github.com/shar0119/acme.sh
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dynu_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
|
||||||
|
Dynu_ClientId=""
|
||||||
|
Dynu_Secret=""
|
||||||
|
_err "Dynu client id and secret is not specified."
|
||||||
|
_err "Please create you API client id and secret and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the client id and secret to the account conf file.
|
||||||
|
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
|
||||||
|
_saveaccountconf Dynu_Secret "$Dynu_Secret"
|
||||||
|
|
||||||
|
if [ -z "$Dynu_Token" ]; then
|
||||||
|
_info "Getting Dynu token."
|
||||||
|
if ! _dynu_authentication; then
|
||||||
|
_err "Can not get token."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Detect root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _node "$_node"
|
||||||
|
_debug _domain_name "$_domain_name"
|
||||||
|
|
||||||
|
_info "Creating TXT record."
|
||||||
|
if ! _dynu_rest POST "dns/$dnsId/record" "{\"domainId\":\"$dnsId\",\"nodeName\":\"$_node\",\"recordType\":\"TXT\",\"textData\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "200"; then
|
||||||
|
_err "Could not add TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dynu_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
|
||||||
|
Dynu_ClientId=""
|
||||||
|
Dynu_Secret=""
|
||||||
|
_err "Dynu client id and secret is not specified."
|
||||||
|
_err "Please create you API client id and secret and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the client id and secret to the account conf file.
|
||||||
|
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
|
||||||
|
_saveaccountconf Dynu_Secret "$Dynu_Secret"
|
||||||
|
|
||||||
|
if [ -z "$Dynu_Token" ]; then
|
||||||
|
_info "Getting Dynu token."
|
||||||
|
if ! _dynu_authentication; then
|
||||||
|
_err "Can not get token."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Detect root zone."
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _node "$_node"
|
||||||
|
_debug _domain_name "$_domain_name"
|
||||||
|
|
||||||
|
_info "Checking for TXT record."
|
||||||
|
if ! _get_recordid "$fulldomain" "$txtvalue"; then
|
||||||
|
_err "Could not get TXT record id."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_dns_record_id" = "" ]; then
|
||||||
|
_err "TXT record not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Removing TXT record."
|
||||||
|
if ! _delete_txt_record "$_dns_record_id"; then
|
||||||
|
_err "Could not remove TXT record $_dns_record_id."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
######## Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _node=_acme-challenge.www
|
||||||
|
# _domain_name=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _dynu_rest GET "dns/getroot/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
|
||||||
|
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
|
||||||
|
_domain_name=$h
|
||||||
|
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_recordid() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _dynu_rest GET "dns/$dnsId/record"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtvalue"; then
|
||||||
|
_dns_record_id=0
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dns_record_id=$(printf "%s" "$response" | sed -e 's/[^{]*\({[^}]*}\)[^{]*/\1\n/g' | grep "\"textData\":\"$txtvalue\"" | sed -e 's/.*"id":\([^,]*\).*/\1/')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_delete_txt_record() {
|
||||||
|
_dns_record_id=$1
|
||||||
|
|
||||||
|
if ! _dynu_rest DELETE "dns/$dnsId/record/$_dns_record_id"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "200"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dynu_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $Dynu_Token"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$data" ] || [ "$m" = "DELETE" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
_info "Getting $Dynu_EndPoint/$ep"
|
||||||
|
response="$(_get "$Dynu_EndPoint/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dynu_authentication() {
|
||||||
|
realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)"
|
||||||
|
|
||||||
|
export _H1="Authorization: Basic $realm"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
response="$(_get "$Dynu_EndPoint/oauth2/token")"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Authentication failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" "Authentication Exception"; then
|
||||||
|
_err "Authentication failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" "access_token"; then
|
||||||
|
Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
|
||||||
|
fi
|
||||||
|
if _contains "$Dynu_Token" "null"; then
|
||||||
|
Dynu_Token=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
285
dnsapi/dns_dynv6.sh
Normal file
285
dnsapi/dns_dynv6.sh
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#Author StefanAbl
|
||||||
|
#Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
|
||||||
|
#or use the HTTP REST API by by specifying a token 'export DYNV6_TOKEN="value"
|
||||||
|
#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
|
||||||
|
|
||||||
|
dynv6_api="https://dynv6.com/api/v2"
|
||||||
|
######## Public functions #####################
|
||||||
|
# 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"
|
||||||
|
dns_dynv6_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using dynv6 api"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
_get_authentication
|
||||||
|
if [ "$dynv6_token" ]; then
|
||||||
|
_dns_dynv6_add_http
|
||||||
|
return $?
|
||||||
|
else
|
||||||
|
_info "using key file $dynv6_keyfile"
|
||||||
|
_your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
|
||||||
|
if ! _get_domain "$fulldomain" "$_your_hosts"; then
|
||||||
|
_err "Host not found on your account"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "found host on your account"
|
||||||
|
returnval="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts \""$_host"\" records set \""$_record"\" txt data \""$txtvalue"\")"
|
||||||
|
_debug "Dynv6 returned this after record was added: $returnval"
|
||||||
|
if _contains "$returnval" "created"; then
|
||||||
|
return 0
|
||||||
|
elif _contains "$returnval" "updated"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Something went wrong! it does not seem like the record was added successfully"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_dynv6_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using dynv6 API"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
_get_authentication
|
||||||
|
if [ "$dynv6_token" ]; then
|
||||||
|
_dns_dynv6_rm_http
|
||||||
|
return $?
|
||||||
|
else
|
||||||
|
_info "using key file $dynv6_keyfile"
|
||||||
|
_your_hosts="$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts)"
|
||||||
|
if ! _get_domain "$fulldomain" "$_your_hosts"; then
|
||||||
|
_err "Host not found on your account"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "found host on your account"
|
||||||
|
_info "$(ssh -i "$dynv6_keyfile" api@dynv6.com hosts "\"$_host\"" records del "\"$_record\"" txt)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#Usage: No Input required
|
||||||
|
#returns
|
||||||
|
#dynv6_keyfile the path to the new key file that has been generated
|
||||||
|
_generate_new_key() {
|
||||||
|
dynv6_keyfile="$(eval echo ~"$USER")/.ssh/dynv6"
|
||||||
|
_info "Path to key file used: $dynv6_keyfile"
|
||||||
|
if [ ! -f "$dynv6_keyfile" ] && [ ! -f "$dynv6_keyfile.pub" ]; then
|
||||||
|
_debug "generating key in $dynv6_keyfile and $dynv6_keyfile.pub"
|
||||||
|
ssh-keygen -f "$dynv6_keyfile" -t ssh-ed25519 -N ''
|
||||||
|
else
|
||||||
|
_err "There is already a file in $dynv6_keyfile or $dynv6_keyfile.pub"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: _acme-challenge.www.example.dynv6.net "$_your_hosts"
|
||||||
|
#where _your_hosts is the output of ssh -i ~/.ssh/dynv6.pub api@dynv6.com hosts
|
||||||
|
#returns
|
||||||
|
#_host= example.dynv6.net
|
||||||
|
#_record=_acme-challenge.www
|
||||||
|
#aborts if not a valid domain
|
||||||
|
_get_domain() {
|
||||||
|
#_your_hosts="$(ssh -i ~/.ssh/dynv6.pub api@dynv6.com hosts)"
|
||||||
|
_full_domain="$1"
|
||||||
|
_your_hosts="$2"
|
||||||
|
|
||||||
|
_your_hosts="$(echo "$_your_hosts" | awk '/\./ {print $1}')"
|
||||||
|
for l in $_your_hosts; do
|
||||||
|
#echo "host: $l"
|
||||||
|
if test "${_full_domain#*$l}" != "$_full_domain"; then
|
||||||
|
_record="${_full_domain%.$l}"
|
||||||
|
_host=$l
|
||||||
|
_debug "The host is $_host and the record $_record"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
_err "Either their is no such host on your dnyv6 account or it cannot be accessed with this key"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: No input required
|
||||||
|
#returns
|
||||||
|
#dynv6_keyfile path to the key that will be used
|
||||||
|
_get_authentication() {
|
||||||
|
dynv6_token="${DYNV6_TOKEN:-$(_readaccountconf_mutable dynv6_token)}"
|
||||||
|
if [ "$dynv6_token" ]; then
|
||||||
|
_debug "Found HTTP Token. Going to use the HTTP API and not the SSH API"
|
||||||
|
if [ "$DYNV6_TOKEN" ]; then
|
||||||
|
_saveaccountconf_mutable dynv6_token "$dynv6_token"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_debug "no HTTP token found. Looking for an SSH key"
|
||||||
|
dynv6_keyfile="${dynv6_keyfile:-$(_readaccountconf_mutable dynv6_keyfile)}"
|
||||||
|
_debug "Your key is $dynv6_keyfile"
|
||||||
|
if [ -z "$dynv6_keyfile" ]; then
|
||||||
|
if [ -z "$KEY" ]; then
|
||||||
|
_err "You did not specify a key to use with dynv6"
|
||||||
|
_info "Creating new dynv6 API key to add to dynv6.com"
|
||||||
|
_generate_new_key
|
||||||
|
_info "Please add this key to dynv6.com $(cat "$dynv6_keyfile.pub")"
|
||||||
|
_info "Hit Enter to continue"
|
||||||
|
read -r _
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
else
|
||||||
|
dynv6_keyfile="$KEY"
|
||||||
|
fi
|
||||||
|
_saveaccountconf_mutable dynv6_keyfile "$dynv6_keyfile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_dynv6_add_http() {
|
||||||
|
_debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
|
||||||
|
if ! _get_zone_id "$fulldomain"; then
|
||||||
|
_err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_get_zone_name "$_zone_id"
|
||||||
|
record="${fulldomain%%.$_zone_name}"
|
||||||
|
_set_record TXT "$record" "$txtvalue"
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Successfully added record"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Something went wrong while adding the record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_dynv6_rm_http() {
|
||||||
|
_debug "Got HTTP token form _get_authentication method. Going to use the HTTP API"
|
||||||
|
if ! _get_zone_id "$fulldomain"; then
|
||||||
|
_err "Could not find a matching zone for $fulldomain. Maybe your HTTP Token is not authorized to access the zone"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_get_zone_name "$_zone_id"
|
||||||
|
record="${fulldomain%%.$_zone_name}"
|
||||||
|
_get_record_id "$_zone_id" "$record" "$txtvalue"
|
||||||
|
_del_record "$_zone_id" "$_record_id"
|
||||||
|
if [ -z "$response" ]; then
|
||||||
|
_info "Successfully deleted record"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Something went wrong while deleting the record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#get the zoneid for a specifc record or zone
|
||||||
|
#usage: _get_zone_id §record
|
||||||
|
#where $record is the record to get the id for
|
||||||
|
#returns _zone_id the id of the zone
|
||||||
|
_get_zone_id() {
|
||||||
|
record="$1"
|
||||||
|
_debug "getting zone id for $record"
|
||||||
|
_dynv6_rest GET zones
|
||||||
|
|
||||||
|
zones="$(echo "$response" | tr '}' '\n' | tr ',' '\n' | grep name | sed 's/\[//g' | tr -d '{' | tr -d '"')"
|
||||||
|
#echo $zones
|
||||||
|
|
||||||
|
selected=""
|
||||||
|
for z in $zones; do
|
||||||
|
z="${z#name:}"
|
||||||
|
_debug zone: "$z"
|
||||||
|
if _contains "$record" "$z"; then
|
||||||
|
_debug "$z found in $record"
|
||||||
|
selected="$z"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "$selected" ]; then
|
||||||
|
_err "no zone found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
|
||||||
|
_zone_id="${zone_id#id:}"
|
||||||
|
_debug "zone id: $_zone_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_zone_name() {
|
||||||
|
_zone_id="$1"
|
||||||
|
_dynv6_rest GET zones/"$_zone_id"
|
||||||
|
_zone_name="$(echo "$response" | tr ',' '\n' | tr -d '{' | grep name | tr -d '"')"
|
||||||
|
_zone_name="${_zone_name#name:}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#usaage _get_record_id $zone_id $record
|
||||||
|
# where zone_id is thevalue returned by _get_zone_id
|
||||||
|
# and record ist in the form _acme.www for an fqdn of _acme.www.example.com
|
||||||
|
# returns _record_id
|
||||||
|
_get_record_id() {
|
||||||
|
_zone_id="$1"
|
||||||
|
record="$2"
|
||||||
|
value="$3"
|
||||||
|
_dynv6_rest GET "zones/$_zone_id/records"
|
||||||
|
if ! _get_record_id_from_response "$response"; then
|
||||||
|
_err "no such record $record found in zone $_zone_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_record_id_from_response() {
|
||||||
|
response="$1"
|
||||||
|
_record_id="$(echo "$response" | tr '}' '\n' | grep "\"name\":\"$record\"" | grep "\"data\":\"$value\"" | tr ',' '\n' | grep id | tr -d '"' | tr -d 'id:')"
|
||||||
|
#_record_id="${_record_id#id:}"
|
||||||
|
if [ -z "$_record_id" ]; then
|
||||||
|
_err "no such record: $record found in zone $_zone_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "record id: $_record_id"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
#usage: _set_record TXT _acme_challenge.www longvalue 12345678
|
||||||
|
#zone id is optional can also be set as vairable bevor calling this method
|
||||||
|
_set_record() {
|
||||||
|
type="$1"
|
||||||
|
record="$2"
|
||||||
|
value="$3"
|
||||||
|
if [ "$4" ]; then
|
||||||
|
_zone_id="$4"
|
||||||
|
fi
|
||||||
|
data="{\"name\": \"$record\", \"data\": \"$value\", \"type\": \"$type\"}"
|
||||||
|
#data='{ "name": "acme.test.thorn.dynv6.net", "type": "A", "data": "192.168.0.1"}'
|
||||||
|
echo "$data"
|
||||||
|
#"{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"
|
||||||
|
_dynv6_rest POST "zones/$_zone_id/records" "$data"
|
||||||
|
}
|
||||||
|
_del_record() {
|
||||||
|
_zone_id=$1
|
||||||
|
_record_id=$2
|
||||||
|
_dynv6_rest DELETE zones/"$_zone_id"/records/"$_record_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
_dynv6_rest() {
|
||||||
|
m=$1 #method GET,POST,DELETE or PUT
|
||||||
|
ep="$2" #the endpoint
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
token_trimmed=$(echo "$dynv6_token" | tr -d '"')
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $token_trimmed"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$dynv6_api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$dynv6_api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
171
dnsapi/dns_easydns.sh
Normal file
171
dnsapi/dns_easydns.sh
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
#
|
||||||
|
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
|
||||||
|
#
|
||||||
|
# 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 #################
|
||||||
|
|
||||||
|
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
#EASYDNS_Token="xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
EASYDNS_Api="https://rest.easydns.net"
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_easydns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
EASYDNS_Token="${EASYDNS_Token:-$(_readaccountconf_mutable EASYDNS_Token)}"
|
||||||
|
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
|
||||||
|
|
||||||
|
if [ -z "$EASYDNS_Token" ] || [ -z "$EASYDNS_Key" ]; then
|
||||||
|
_err "You didn't specify an easydns.net token or api key. Signup at https://cp.easydns.com/manage/security/api/signup.php"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_saveaccountconf_mutable EASYDNS_Token "$EASYDNS_Token"
|
||||||
|
_saveaccountconf_mutable EASYDNS_Key "$EASYDNS_Key"
|
||||||
|
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"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_EASYDNS_rest GET "zones/records/all/${_domain}/search/${_sub_domain}"
|
||||||
|
|
||||||
|
if ! printf "%s" "$response" | grep \"status\":200 >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _EASYDNS_rest PUT "zones/records/add/$_domain/TXT" "{\"host\":\"$_sub_domain\",\"rdata\":\"$txtvalue\"}"; then
|
||||||
|
if _contains "$response" "\"status\":201"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
elif _contains "$response" "Record already exists"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_easydns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
EASYDNS_Token="${EASYDNS_Token:-$(_readaccountconf_mutable EASYDNS_Token)}"
|
||||||
|
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
|
||||||
|
|
||||||
|
_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 txt records"
|
||||||
|
_EASYDNS_rest GET "zones/records/all/${_domain}/search/${_sub_domain}"
|
||||||
|
|
||||||
|
if ! printf "%s" "$response" | grep \"status\":200 >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
|
||||||
|
_debug count "$count"
|
||||||
|
if [ "$count" = "0" ]; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
else
|
||||||
|
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
|
||||||
|
_debug "record_id" "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _EASYDNS_rest DELETE "zones/records/$_domain/$record_id"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_contains "$response" "\"status\":200"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _EASYDNS_rest GET "zones/records/all/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"status\":200"; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_EASYDNS_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
basicauth=$(printf "%s" "$EASYDNS_Token":"$EASYDNS_Key" | _base64)
|
||||||
|
|
||||||
|
export _H1="accept: application/json"
|
||||||
|
if [ "$basicauth" ]; then
|
||||||
|
export _H2="Authorization: Basic $basicauth"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$EASYDNS_Api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$EASYDNS_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
466
dnsapi/dns_edgedns.sh
Executable file
466
dnsapi/dns_edgedns.sh
Executable file
@@ -0,0 +1,466 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Akamai Edge DNS v2 API
|
||||||
|
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
|
||||||
|
# Edge DNS Zones and Recordsets, e.g. DNS—Zone Record Management authorization
|
||||||
|
|
||||||
|
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
|
||||||
|
|
||||||
|
# Values to export:
|
||||||
|
# --EITHER--
|
||||||
|
# *** TBD. NOT IMPLEMENTED YET ***
|
||||||
|
# specify Edgegrid credentials file and section
|
||||||
|
# AKAMAI_EDGERC=<full file path>
|
||||||
|
# AKAMAI_EDGERC_SECTION="default"
|
||||||
|
## --OR--
|
||||||
|
# specify indiviual credentials
|
||||||
|
# export AKAMAI_HOST = <host>
|
||||||
|
# export AKAMAI_ACCESS_TOKEN = <access token>
|
||||||
|
# export AKAMAI_CLIENT_TOKEN = <client token>
|
||||||
|
# export AKAMAI_CLIENT_SECRET = <client secret>
|
||||||
|
|
||||||
|
ACME_EDGEDNS_VERSION="0.1.0"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: dns_edgedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
#
|
||||||
|
dns_edgedns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "ENTERING DNS_EDGEDNS_ADD"
|
||||||
|
_debug2 "fulldomain" "$fulldomain"
|
||||||
|
_debug2 "txtvalue" "$txtvalue"
|
||||||
|
|
||||||
|
if ! _EDGEDNS_credentials; then
|
||||||
|
_err "$@"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 "Add: zone" "$zone"
|
||||||
|
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "$edge_endpoint" "$zone" "$fulldomain")
|
||||||
|
_debug3 "Add URL" "$acmeRecordURI"
|
||||||
|
# Get existing TXT record
|
||||||
|
_edge_result=$(_edgedns_rest GET "$acmeRecordURI")
|
||||||
|
_api_status="$?"
|
||||||
|
_debug3 "_edge_result" "$_edge_result"
|
||||||
|
if [ "$_api_status" -ne 0 ]; then
|
||||||
|
if [ "$curResult" = "FATAL" ]; then
|
||||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
|
||||||
|
fi
|
||||||
|
if [ "$_edge_result" != "404" ]; then
|
||||||
|
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rdata="\"${txtvalue}\""
|
||||||
|
record_op="POST"
|
||||||
|
if [ "$_api_status" -eq 0 ]; then
|
||||||
|
# record already exists. Get existing record data and update
|
||||||
|
record_op="PUT"
|
||||||
|
rdlist="${_edge_result#*\"rdata\":[}"
|
||||||
|
rdlist="${rdlist%%]*}"
|
||||||
|
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
|
||||||
|
_debug3 "existing TXT found"
|
||||||
|
_debug3 "record data" "$rdlist"
|
||||||
|
# value already there?
|
||||||
|
if _contains "$rdlist" "$txtvalue"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_txt_val=""
|
||||||
|
while [ "$_txt_val" != "$rdlist" ] && [ "${rdlist}" ]; do
|
||||||
|
_txt_val="${rdlist%%,*}"
|
||||||
|
rdlist="${rdlist#*,}"
|
||||||
|
rdata="${rdata},\"${_txt_val}\""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
# Add the txtvalue TXT Record
|
||||||
|
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
|
||||||
|
_debug3 "Add body '${body}'"
|
||||||
|
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
|
||||||
|
_api_status="$?"
|
||||||
|
if [ "$_api_status" -eq 0 ]; then
|
||||||
|
_log "$(printf "Text value %s added to recordset %s" "$txtvalue" "$fulldomain")"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "$(printf "error adding TXT record for validation. Error: %s" "$_edge_result")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: dns_edgedns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to delete txt record
|
||||||
|
#
|
||||||
|
dns_edgedns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "ENTERING DNS_EDGEDNS_RM"
|
||||||
|
_debug2 "fulldomain" "$fulldomain"
|
||||||
|
_debug2 "txtvalue" "$txtvalue"
|
||||||
|
|
||||||
|
if ! _EDGEDNS_credentials; then
|
||||||
|
_err "$@"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 "RM: zone" "${zone}"
|
||||||
|
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "${edge_endpoint}" "$zone" "$fulldomain")
|
||||||
|
_debug3 "RM URL" "$acmeRecordURI"
|
||||||
|
# Get existing TXT record
|
||||||
|
_edge_result=$(_edgedns_rest GET "$acmeRecordURI")
|
||||||
|
_api_status="$?"
|
||||||
|
if [ "$_api_status" -ne 0 ]; then
|
||||||
|
if [ "$curResult" = "FATAL" ]; then
|
||||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
|
||||||
|
fi
|
||||||
|
if [ "$_edge_result" != "404" ]; then
|
||||||
|
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_debug3 "_edge_result" "$_edge_result"
|
||||||
|
record_op="DELETE"
|
||||||
|
body=""
|
||||||
|
if [ "$_api_status" -eq 0 ]; then
|
||||||
|
# record already exists. Get existing record data and update
|
||||||
|
rdlist="${_edge_result#*\"rdata\":[}"
|
||||||
|
rdlist="${rdlist%%]*}"
|
||||||
|
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
|
||||||
|
_debug3 "rdlist" "$rdlist"
|
||||||
|
if [ -n "$rdlist" ]; then
|
||||||
|
record_op="PUT"
|
||||||
|
comma=""
|
||||||
|
rdata=""
|
||||||
|
_txt_val=""
|
||||||
|
while [ "$_txt_val" != "$rdlist" ] && [ "$rdlist" ]; do
|
||||||
|
_txt_val="${rdlist%%,*}"
|
||||||
|
rdlist="${rdlist#*,}"
|
||||||
|
_debug3 "_txt_val" "$_txt_val"
|
||||||
|
_debug3 "txtvalue" "$txtvalue"
|
||||||
|
if ! _contains "$_txt_val" "$txtvalue"; then
|
||||||
|
rdata="${rdata}${comma}\"${_txt_val}\""
|
||||||
|
comma=","
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "$rdata" ]; then
|
||||||
|
record_op="DELETE"
|
||||||
|
else
|
||||||
|
# Recreate the txtvalue TXT Record
|
||||||
|
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
|
||||||
|
_debug3 "body" "$body"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
|
||||||
|
_api_status="$?"
|
||||||
|
if [ "$_api_status" -eq 0 ]; then
|
||||||
|
_log "$(printf "Text value %s removed from recordset %s" "$txtvalue" "$fulldomain")"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "$(printf "error removing TXT record for validation. Error: %s" "$_edge_result")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_EDGEDNS_credentials() {
|
||||||
|
_debug "GettingEdge DNS credentials"
|
||||||
|
_log "$(printf "ACME DNSAPI Edge DNS version %s" ${ACME_EDGEDNS_VERSION})"
|
||||||
|
args_missing=0
|
||||||
|
if [ -z "$AKAMAI_ACCESS_TOKEN" ]; then
|
||||||
|
AKAMAI_ACCESS_TOKEN=""
|
||||||
|
AKAMAI_CLIENT_TOKEN=""
|
||||||
|
AKAMAI_HOST=""
|
||||||
|
AKAMAI_CLIENT_SECRET=""
|
||||||
|
_err "AKAMAI_ACCESS_TOKEN is missing"
|
||||||
|
args_missing=1
|
||||||
|
fi
|
||||||
|
if [ -z "$AKAMAI_CLIENT_TOKEN" ]; then
|
||||||
|
AKAMAI_ACCESS_TOKEN=""
|
||||||
|
AKAMAI_CLIENT_TOKEN=""
|
||||||
|
AKAMAI_HOST=""
|
||||||
|
AKAMAI_CLIENT_SECRET=""
|
||||||
|
_err "AKAMAI_CLIENT_TOKEN is missing"
|
||||||
|
args_missing=1
|
||||||
|
fi
|
||||||
|
if [ -z "$AKAMAI_HOST" ]; then
|
||||||
|
AKAMAI_ACCESS_TOKEN=""
|
||||||
|
AKAMAI_CLIENT_TOKEN=""
|
||||||
|
AKAMAI_HOST=""
|
||||||
|
AKAMAI_CLIENT_SECRET=""
|
||||||
|
_err "AKAMAI_HOST is missing"
|
||||||
|
args_missing=1
|
||||||
|
fi
|
||||||
|
if [ -z "$AKAMAI_CLIENT_SECRET" ]; then
|
||||||
|
AKAMAI_ACCESS_TOKEN=""
|
||||||
|
AKAMAI_CLIENT_TOKEN=""
|
||||||
|
AKAMAI_HOST=""
|
||||||
|
AKAMAI_CLIENT_SECRET=""
|
||||||
|
_err "AKAMAI_CLIENT_SECRET is missing"
|
||||||
|
args_missing=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$args_missing" = 1 ]; then
|
||||||
|
_err "You have not properly specified the EdgeDNS Open Edgegrid API credentials. Please try again."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_saveaccountconf_mutable AKAMAI_ACCESS_TOKEN "$AKAMAI_ACCESS_TOKEN"
|
||||||
|
_saveaccountconf_mutable AKAMAI_CLIENT_TOKEN "$AKAMAI_CLIENT_TOKEN"
|
||||||
|
_saveaccountconf_mutable AKAMAI_HOST "$AKAMAI_HOST"
|
||||||
|
_saveaccountconf_mutable AKAMAI_CLIENT_SECRET "$AKAMAI_CLIENT_SECRET"
|
||||||
|
# Set whether curl should use secure or insecure mode
|
||||||
|
fi
|
||||||
|
export HTTPS_INSECURE=0 # All Edgegrid API calls are secure
|
||||||
|
edge_endpoint=$(printf "https://%s/config-dns/v2/zones" "$AKAMAI_HOST")
|
||||||
|
_debug3 "Edge API Endpoint:" "$edge_endpoint"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_EDGEDNS_getZoneInfo() {
|
||||||
|
_debug "Getting Zoneinfo"
|
||||||
|
zoneEnd=false
|
||||||
|
curZone=$1
|
||||||
|
while [ -n "$zoneEnd" ]; do
|
||||||
|
# we can strip the first part of the fulldomain, since its just the _acme-challenge string
|
||||||
|
curZone="${curZone#*.}"
|
||||||
|
# suffix . needed for zone -> domain.tld.
|
||||||
|
# create zone get url
|
||||||
|
get_zone_url=$(printf "%s/%s" "$edge_endpoint" "$curZone")
|
||||||
|
_debug3 "Zone Get: " "${get_zone_url}"
|
||||||
|
curResult=$(_edgedns_rest GET "$get_zone_url")
|
||||||
|
retVal=$?
|
||||||
|
if [ "$retVal" -ne 0 ]; then
|
||||||
|
if [ "$curResult" = "FATAL" ]; then
|
||||||
|
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
|
||||||
|
fi
|
||||||
|
if [ "$curResult" != "404" ]; then
|
||||||
|
_err "$(printf "Managed zone validation failed. Error response: %s" "$retVal")"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if _contains "$curResult" "\"zone\":"; then
|
||||||
|
_debug2 "Zone data" "${curResult}"
|
||||||
|
zone=$(echo "${curResult}" | _egrep_o "\"zone\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
|
||||||
|
_debug3 "Zone" "${zone}"
|
||||||
|
zoneEnd=""
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${curZone#*.}" != "$curZone" ]; then
|
||||||
|
_debug3 "$(printf "%s still contains a '.' - so we can check next higher level" "$curZone")"
|
||||||
|
else
|
||||||
|
zoneEnd=true
|
||||||
|
_err "Couldn't retrieve zone data."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
_err "Failed to retrieve zone data."
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_headers=""
|
||||||
|
|
||||||
|
_edgedns_rest() {
|
||||||
|
_debug "Handling API Request"
|
||||||
|
m=$1
|
||||||
|
# Assume endpoint is complete path, including query args if applicable
|
||||||
|
ep=$2
|
||||||
|
body_data=$3
|
||||||
|
_edgedns_content_type=""
|
||||||
|
_request_url_path="$ep"
|
||||||
|
_request_body="$body_data"
|
||||||
|
_request_method="$m"
|
||||||
|
_edgedns_headers=""
|
||||||
|
tab=""
|
||||||
|
_edgedns_headers="${_edgedns_headers}${tab}Host: ${AKAMAI_HOST}"
|
||||||
|
tab="\t"
|
||||||
|
# Set in acme.sh _post/_get
|
||||||
|
#_edgedns_headers="${_edgedns_headers}${tab}User-Agent:ACME DNSAPI Edge DNS version ${ACME_EDGEDNS_VERSION}"
|
||||||
|
_edgedns_headers="${_edgedns_headers}${tab}Accept: application/json,*/*"
|
||||||
|
if [ "$m" != "GET" ] && [ "$m" != "DELETE" ]; then
|
||||||
|
_edgedns_content_type="application/json"
|
||||||
|
_debug3 "_request_body" "$_request_body"
|
||||||
|
_body_len=$(echo "$_request_body" | tr -d "\n\r" | awk '{print length}')
|
||||||
|
_edgedns_headers="${_edgedns_headers}${tab}Content-Length: ${_body_len}"
|
||||||
|
fi
|
||||||
|
_edgedns_make_auth_header
|
||||||
|
_edgedns_headers="${_edgedns_headers}${tab}Authorization: ${_signed_auth_header}"
|
||||||
|
_secure_debug2 "Made Auth Header" "$_signed_auth_header"
|
||||||
|
hdr_indx=1
|
||||||
|
work_header="${_edgedns_headers}${tab}"
|
||||||
|
_debug3 "work_header" "$work_header"
|
||||||
|
while [ "$work_header" ]; do
|
||||||
|
entry="${work_header%%\\t*}"
|
||||||
|
work_header="${work_header#*\\t}"
|
||||||
|
export "$(printf "_H%s=%s" "$hdr_indx" "$entry")"
|
||||||
|
_debug2 "Request Header " "$entry"
|
||||||
|
hdr_indx=$((hdr_indx + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# clear headers from previous request to avoid getting wrong http code on timeouts
|
||||||
|
: >"$HTTP_HEADER"
|
||||||
|
_debug2 "$ep"
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug3 "Method data" "$data"
|
||||||
|
# body url [needbase64] [POST|PUT|DELETE] [ContentType]
|
||||||
|
response=$(_post "$_request_body" "$ep" false "$m" "$_edgedns_content_type")
|
||||||
|
else
|
||||||
|
response=$(_get "$ep")
|
||||||
|
fi
|
||||||
|
_ret="$?"
|
||||||
|
if [ "$_ret" -ne 0 ]; then
|
||||||
|
_err "$(printf "acme.sh API function call failed. Error: %s" "$_ret")"
|
||||||
|
echo "FATAL"
|
||||||
|
return "$_ret"
|
||||||
|
fi
|
||||||
|
_debug2 "response" "${response}"
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
_debug2 "http response code" "$_code"
|
||||||
|
if [ "$_code" = "200" ] || [ "$_code" = "201" ]; then
|
||||||
|
# All good
|
||||||
|
response="$(echo "${response}" | _normalizeJson)"
|
||||||
|
echo "$response"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" = "204" ]; then
|
||||||
|
# Success, no body
|
||||||
|
echo "$_code"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" = "400" ]; then
|
||||||
|
_err "Bad request presented"
|
||||||
|
_log "$(printf "Headers: %s" "$_edgedns_headers")"
|
||||||
|
_log "$(printf "Method: %s" "$_request_method")"
|
||||||
|
_log "$(printf "URL: %s" "$ep")"
|
||||||
|
_log "$(printf "Data: %s" "$data")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" = "403" ]; then
|
||||||
|
_err "access denied make sure your Edgegrid cedentials are correct."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$_code"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_eg_timestamp() {
|
||||||
|
_debug "Generating signature Timestamp"
|
||||||
|
_debug3 "Retriving ntp time"
|
||||||
|
_timeheaders="$(_get "https://www.ntp.org" "onlyheader")"
|
||||||
|
_debug3 "_timeheaders" "$_timeheaders"
|
||||||
|
_ntpdate="$(echo "$_timeheaders" | grep -i "Date:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
|
||||||
|
_debug3 "_ntpdate" "$_ntpdate"
|
||||||
|
_ntpdate="$(echo "${_ntpdate}" | sed -e 's/^[[:space:]]*//')"
|
||||||
|
_debug3 "_NTPDATE" "$_ntpdate"
|
||||||
|
_ntptime="$(echo "${_ntpdate}" | _head_n 1 | cut -d " " -f 5 | tr -d "\r\n")"
|
||||||
|
_debug3 "_ntptime" "$_ntptime"
|
||||||
|
_eg_timestamp=$(date -u "+%Y%m%dT")
|
||||||
|
_eg_timestamp="$(printf "%s%s+0000" "$_eg_timestamp" "$_ntptime")"
|
||||||
|
_debug "_eg_timestamp" "$_eg_timestamp"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_new_nonce() {
|
||||||
|
_debug "Generating Nonce"
|
||||||
|
_nonce=$(echo "EDGEDNS$(_time)" | _digest sha1 hex | cut -c 1-32)
|
||||||
|
_debug3 "_nonce" "$_nonce"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_make_auth_header() {
|
||||||
|
_debug "Constructing Auth Header"
|
||||||
|
_edgedns_new_nonce
|
||||||
|
_edgedns_eg_timestamp
|
||||||
|
# "Unsigned authorization header: 'EG1-HMAC-SHA256 client_token=block;access_token=block;timestamp=20200806T14:16:33+0000;nonce=72cde72c-82d9-4721-9854-2ba057929d67;'"
|
||||||
|
_auth_header="$(printf "EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;" "$AKAMAI_CLIENT_TOKEN" "$AKAMAI_ACCESS_TOKEN" "$_eg_timestamp" "$_nonce")"
|
||||||
|
_secure_debug2 "Unsigned Auth Header: " "$_auth_header"
|
||||||
|
|
||||||
|
_edgedns_sign_request
|
||||||
|
_signed_auth_header="$(printf "%ssignature=%s" "$_auth_header" "$_signed_req")"
|
||||||
|
_secure_debug2 "Signed Auth Header: " "${_signed_auth_header}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_sign_request() {
|
||||||
|
_debug2 "Signing http request"
|
||||||
|
_edgedns_make_data_to_sign "$_auth_header"
|
||||||
|
_secure_debug2 "Returned signed data" "$_mdata"
|
||||||
|
_edgedns_make_signing_key "$_eg_timestamp"
|
||||||
|
_edgedns_base64_hmac_sha256 "$_mdata" "$_signing_key"
|
||||||
|
_signed_req="$_hmac_out"
|
||||||
|
_secure_debug2 "Signed Request" "$_signed_req"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_make_signing_key() {
|
||||||
|
_debug2 "Creating sigining key"
|
||||||
|
ts=$1
|
||||||
|
_edgedns_base64_hmac_sha256 "$ts" "$AKAMAI_CLIENT_SECRET"
|
||||||
|
_signing_key="$_hmac_out"
|
||||||
|
_secure_debug2 "Signing Key" "$_signing_key"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_make_data_to_sign() {
|
||||||
|
_debug2 "Processing data to sign"
|
||||||
|
hdr=$1
|
||||||
|
_secure_debug2 "hdr" "$hdr"
|
||||||
|
_edgedns_make_content_hash
|
||||||
|
path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')"
|
||||||
|
path="${path#*$AKAMAI_HOST}"
|
||||||
|
_debug "hier path" "$path"
|
||||||
|
# dont expose headers to sign so use MT string
|
||||||
|
_mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")"
|
||||||
|
_secure_debug2 "Data to Sign" "$_mdata"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_make_content_hash() {
|
||||||
|
_debug2 "Generating content hash"
|
||||||
|
_hash=""
|
||||||
|
_debug2 "Request method" "${_request_method}"
|
||||||
|
if [ "$_request_method" != "POST" ] || [ -z "$_request_body" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug2 "Req body" "$_request_body"
|
||||||
|
_edgedns_base64_sha256 "$_request_body"
|
||||||
|
_hash="$_sha256_out"
|
||||||
|
_debug2 "Content hash" "$_hash"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_base64_hmac_sha256() {
|
||||||
|
_debug2 "Generating hmac"
|
||||||
|
data=$1
|
||||||
|
key=$2
|
||||||
|
encoded_data="$(echo "$data" | iconv -t utf-8)"
|
||||||
|
encoded_key="$(echo "$key" | iconv -t utf-8)"
|
||||||
|
_secure_debug2 "encoded data" "$encoded_data"
|
||||||
|
_secure_debug2 "encoded key" "$encoded_key"
|
||||||
|
|
||||||
|
encoded_key_hex=$(printf "%s" "$encoded_key" | _hex_dump | tr -d ' ')
|
||||||
|
data_sig="$(echo "$encoded_data" | tr -d "\n\r" | _hmac sha256 "$encoded_key_hex" | _base64)"
|
||||||
|
|
||||||
|
_secure_debug2 "data_sig:" "$data_sig"
|
||||||
|
_hmac_out="$(echo "$data_sig" | tr -d "\n\r" | iconv -f utf-8)"
|
||||||
|
_secure_debug2 "hmac" "$_hmac_out"
|
||||||
|
}
|
||||||
|
|
||||||
|
_edgedns_base64_sha256() {
|
||||||
|
_debug2 "Creating sha256 digest"
|
||||||
|
trg=$1
|
||||||
|
_secure_debug2 "digest data" "$trg"
|
||||||
|
digest="$(echo "$trg" | tr -d "\n\r" | _digest "sha256")"
|
||||||
|
_sha256_out="$(echo "$digest" | tr -d "\n\r" | iconv -f utf-8)"
|
||||||
|
_secure_debug2 "digest decode" "$_sha256_out"
|
||||||
|
}
|
||||||
|
|
||||||
|
#_edgedns_parse_edgerc() {
|
||||||
|
# filepath=$1
|
||||||
|
# section=$2
|
||||||
|
#}
|
||||||
358
dnsapi/dns_euserv.sh
Normal file
358
dnsapi/dns_euserv.sh
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#This is the euserv.eu api wrapper for acme.sh
|
||||||
|
#
|
||||||
|
#Author: Michael Brueckner
|
||||||
|
#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de
|
||||||
|
|
||||||
|
#
|
||||||
|
#EUSERV_Username="username"
|
||||||
|
#
|
||||||
|
#EUSERV_Password="password"
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# -------------
|
||||||
|
# - none -
|
||||||
|
|
||||||
|
EUSERV_Api="https://api.euserv.net"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_euserv_add() {
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
|
||||||
|
EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
|
||||||
|
if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
|
||||||
|
EUSERV_Username=""
|
||||||
|
EUSERV_Password=""
|
||||||
|
_err "You don't specify euserv user and password yet."
|
||||||
|
_err "Please create your key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the user and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
|
||||||
|
_saveaccountconf_mutable EUSERV_Password "$EUSERV_Password"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "_sub_domain" "$_sub_domain"
|
||||||
|
_debug "_domain" "$_domain"
|
||||||
|
_info "Adding record"
|
||||||
|
if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_euserv_rm() {
|
||||||
|
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
|
||||||
|
EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
|
||||||
|
if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
|
||||||
|
EUSERV_Username=""
|
||||||
|
EUSERV_Password=""
|
||||||
|
_err "You don't specify euserv user and password yet."
|
||||||
|
_err "Please create your key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the user and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
|
||||||
|
_saveaccountconf_mutable EUSERV_Password "$EUSERV_Password"
|
||||||
|
|
||||||
|
_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 txt records"
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>domain.dns_get_active_records</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>login</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>password</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>domain_id</name>
|
||||||
|
<value>
|
||||||
|
<int>%s</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id")
|
||||||
|
|
||||||
|
export _H1="Content-Type: text/xml"
|
||||||
|
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
|
||||||
|
_err "Error could not get txt records"
|
||||||
|
_debug "xml_content" "$xml_content"
|
||||||
|
_debug "response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! echo "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then
|
||||||
|
_info "Do not need to delete record"
|
||||||
|
else
|
||||||
|
# find XML block where txtvalue is in. The record_id is allways prior this line!
|
||||||
|
_endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1)
|
||||||
|
# record_id is the last <name> Tag with a number before the row _endLine, identified by </name><value><struct>
|
||||||
|
_record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '</name><value><struct>' | _tail_n 1 | sed 's/.*<name>\([0-9]*\)<\/name>.*/\1/')
|
||||||
|
_info "Deleting record"
|
||||||
|
_euserv_delete_record "$_record_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
_debug "get root"
|
||||||
|
|
||||||
|
# Just to read the domain_orders once
|
||||||
|
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if ! _euserv_get_domain_orders; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get saved response with domain_orders
|
||||||
|
response="$_euserv_domain_orders"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(echo "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "$h"; then
|
||||||
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain="$h"
|
||||||
|
if ! _euserv_get_domain_id "$_domain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_euserv_get_domain_orders() {
|
||||||
|
# returns: _euserv_domain_orders
|
||||||
|
|
||||||
|
_debug "get domain_orders"
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>domain.get_domain_orders</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>login</name>
|
||||||
|
<value><string>%s</string></value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>password</name>
|
||||||
|
<value><string>%s</string></value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$EUSERV_Username" "$EUSERV_Password")
|
||||||
|
|
||||||
|
export _H1="Content-Type: text/xml"
|
||||||
|
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
|
||||||
|
_err "Error could not get domain orders"
|
||||||
|
_debug "xml_content" "$xml_content"
|
||||||
|
_debug "response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# save response to reduce API calls
|
||||||
|
_euserv_domain_orders="$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_euserv_get_domain_id() {
|
||||||
|
# returns: _euserv_domain_id
|
||||||
|
domain=$1
|
||||||
|
_debug "get domain_id"
|
||||||
|
|
||||||
|
# find line where the domain name is within the $response
|
||||||
|
_startLine=$(echo "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1)
|
||||||
|
# next occurency of domain_id after the domain_name is the correct one
|
||||||
|
_euserv_domain_id=$(echo "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | _head_n 1 | sed 's/.*<i4>\([0-9]*\)<\/i4>.*/\1/')
|
||||||
|
|
||||||
|
if [ -z "$_euserv_domain_id" ]; then
|
||||||
|
_err "Could not find domain_id for domain $domain"
|
||||||
|
_debug "_euserv_domain_orders" "$_euserv_domain_orders"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_euserv_delete_record() {
|
||||||
|
record_id=$1
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>domain.dns_delete_record</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>login</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>password</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>dns_record_id</name>
|
||||||
|
<value>
|
||||||
|
<int>%s</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$record_id")
|
||||||
|
|
||||||
|
export _H1="Content-Type: text/xml"
|
||||||
|
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
|
||||||
|
_err "Error deleting record"
|
||||||
|
_debug "xml_content" "$xml_content"
|
||||||
|
_debug "response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_euserv_add_record() {
|
||||||
|
domain=$1
|
||||||
|
sub_domain=$2
|
||||||
|
txtval=$3
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>domain.dns_create_record</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>login</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>password</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string></value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>domain_id</name>
|
||||||
|
<value>
|
||||||
|
<int>%s</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>dns_record_subdomain</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>dns_record_type</name>
|
||||||
|
<value>
|
||||||
|
<string>TXT</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>dns_record_value</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>dns_record_ttl</name>
|
||||||
|
<value>
|
||||||
|
<int>300</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval")
|
||||||
|
|
||||||
|
export _H1="Content-Type: text/xml"
|
||||||
|
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
|
||||||
|
_err "Error could not create record"
|
||||||
|
_debug "xml_content" "$xml_content"
|
||||||
|
_debug "response" "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
168
dnsapi/dns_exoscale.sh
Executable file
168
dnsapi/dns_exoscale.sh
Executable file
@@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
EXOSCALE_API=https://api.exoscale.com/dns/v1
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_exoscale_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _checkAuth; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_exoscale_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _checkAuth; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
_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
|
||||||
|
|
||||||
|
if [ -z "$_record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Deleting record $_record_id"
|
||||||
|
|
||||||
|
if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_checkAuth() {
|
||||||
|
EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
|
||||||
|
EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
|
||||||
|
|
||||||
|
if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
|
||||||
|
EXOSCALE_API_KEY=""
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
|
||||||
|
_saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#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
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
|
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
_domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
if [ "$_domain_token" ] && [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns response
|
||||||
|
_exoscale_rest() {
|
||||||
|
method=$1
|
||||||
|
path="$2"
|
||||||
|
data="$3"
|
||||||
|
token="$4"
|
||||||
|
request_url="$EXOSCALE_API/$path"
|
||||||
|
_debug "$path"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
|
||||||
|
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
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$request_url" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$request_url" "" "" "$method")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $request_url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
371
dnsapi/dns_freedns.sh
Executable file
371
dnsapi/dns_freedns.sh
Executable file
@@ -0,0 +1,371 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#This file name is "dns_freedns.sh"
|
||||||
|
#So, here must be a method dns_freedns_add()
|
||||||
|
#Which will be called by acme.sh to add the txt record to your api system.
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
#
|
||||||
|
#Author: David Kerr
|
||||||
|
#Report Bugs here: https://github.com/dkerr64/acme.sh
|
||||||
|
#or here... https://github.com/acmesh-official/acme.sh/issues/2305
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Export FreeDNS userid and password in following variables...
|
||||||
|
# FREEDNS_User=username
|
||||||
|
# FREEDNS_Password=password
|
||||||
|
# login cookie is saved in acme account config file so userid / pw
|
||||||
|
# need to be set only when changed.
|
||||||
|
|
||||||
|
#Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_freedns_add() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Add TXT record using FreeDNS"
|
||||||
|
_debug "fulldomain: $fulldomain"
|
||||||
|
_debug "txtvalue: $txtvalue"
|
||||||
|
|
||||||
|
if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then
|
||||||
|
FREEDNS_User=""
|
||||||
|
FREEDNS_Password=""
|
||||||
|
if [ -z "$FREEDNS_COOKIE" ]; then
|
||||||
|
_err "You did not specify the FreeDNS username and password yet."
|
||||||
|
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
using_cached_cookies="true"
|
||||||
|
else
|
||||||
|
FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")"
|
||||||
|
if [ -z "$FREEDNS_COOKIE" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
using_cached_cookies="false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)"
|
||||||
|
|
||||||
|
_saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE"
|
||||||
|
|
||||||
|
# We may have to cycle through the domain name to find the
|
||||||
|
# TLD that we own...
|
||||||
|
i=1
|
||||||
|
wmax="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
|
||||||
|
while [ "$i" -lt "$wmax" ]; do
|
||||||
|
# split our full domain name into two parts...
|
||||||
|
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
|
||||||
|
i="$(_math "$i" + 1)"
|
||||||
|
top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)"
|
||||||
|
_debug "sub_domain: $sub_domain"
|
||||||
|
_debug "top_domain: $top_domain"
|
||||||
|
|
||||||
|
DNSdomainid="$(_freedns_domain_id "$top_domain")"
|
||||||
|
if [ "$?" = "0" ]; then
|
||||||
|
_info "Domain $top_domain found at FreeDNS, domain_id $DNSdomainid"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
_info "Domain $top_domain not found at FreeDNS, try with next level of TLD"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$DNSdomainid" ]; then
|
||||||
|
# If domain ID is empty then something went wrong (top level
|
||||||
|
# domain not found at FreeDNS).
|
||||||
|
_err "Domain $top_domain not found at FreeDNS"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add in new TXT record with the value provided
|
||||||
|
_debug "Adding TXT record for $fulldomain, $txtvalue"
|
||||||
|
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_freedns_rm() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Delete TXT record using FreeDNS"
|
||||||
|
_debug "fulldomain: $fulldomain"
|
||||||
|
_debug "txtvalue: $txtvalue"
|
||||||
|
|
||||||
|
# Need to read cookie from conf file again in case new value set
|
||||||
|
# during login to FreeDNS when TXT record was created.
|
||||||
|
FREEDNS_COOKIE="$(_readaccountconf "FREEDNS_COOKIE")"
|
||||||
|
_debug "FreeDNS login cookies: $FREEDNS_COOKIE"
|
||||||
|
|
||||||
|
TXTdataid="$(_freedns_data_id "$fulldomain" "TXT")"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_info "Cannot delete TXT record for $fulldomain, record does not exist at FreeDNS"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "Data ID's found, $TXTdataid"
|
||||||
|
|
||||||
|
# now we have one (or more) TXT record data ID's. Load the page
|
||||||
|
# for that record and search for the record txt value. If match
|
||||||
|
# then we can delete it.
|
||||||
|
lines="$(echo "$TXTdataid" | wc -l)"
|
||||||
|
_debug "Found $lines TXT data records for $fulldomain"
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt "$lines" ]; do
|
||||||
|
i="$(_math "$i" + 1)"
|
||||||
|
dataid="$(echo "$TXTdataid" | sed -n "${i}p")"
|
||||||
|
_debug "$dataid"
|
||||||
|
|
||||||
|
htmlpage="$(_freedns_retrieve_data_page "$FREEDNS_COOKIE" "$dataid")"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
if [ "$using_cached_cookies" = "true" ]; then
|
||||||
|
_err "Has your FreeDNS username and password changed? If so..."
|
||||||
|
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$htmlpage" | grep "value=\""$txtvalue"\"" >/dev/null
|
||||||
|
if [ "$?" = "0" ]; then
|
||||||
|
# Found a match... delete the record and return
|
||||||
|
_info "Deleting TXT record for $fulldomain, $txtvalue"
|
||||||
|
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$dataid"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If we get this far we did not find a match
|
||||||
|
# Not necessarily an error, but log anyway.
|
||||||
|
_info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# usage: _freedns_login username password
|
||||||
|
# print string "cookie=value" etc.
|
||||||
|
# returns 0 success
|
||||||
|
_freedns_login() {
|
||||||
|
export _H1="Accept-Language:en-US"
|
||||||
|
username="$1"
|
||||||
|
password="$2"
|
||||||
|
url="https://freedns.afraid.org/zc.php?step=2"
|
||||||
|
|
||||||
|
_debug "Login to FreeDNS as user $username"
|
||||||
|
|
||||||
|
htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "FreeDNS login failed for user $username bad RC from _post"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
|
||||||
|
|
||||||
|
# if cookies is not empty then logon successful
|
||||||
|
if [ -z "$cookies" ]; then
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
_err "FreeDNS login failed for user $username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s" "$cookies"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_retrieve_subdomain_page login_cookies
|
||||||
|
# echo page retrieved (html)
|
||||||
|
# returns 0 success
|
||||||
|
_freedns_retrieve_subdomain_page() {
|
||||||
|
export _H1="Cookie:$1"
|
||||||
|
export _H2="Accept-Language:en-US"
|
||||||
|
url="https://freedns.afraid.org/subdomain/"
|
||||||
|
|
||||||
|
_debug "Retrieve subdomain page from FreeDNS"
|
||||||
|
|
||||||
|
htmlpage="$(_get "$url")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "FreeDNS retrieve subdomains failed bad RC from _get"
|
||||||
|
return 1
|
||||||
|
elif [ -z "$htmlpage" ]; then
|
||||||
|
_err "FreeDNS returned empty subdomain page"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
|
||||||
|
printf "%s" "$htmlpage"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_retrieve_data_page login_cookies data_id
|
||||||
|
# echo page retrieved (html)
|
||||||
|
# returns 0 success
|
||||||
|
_freedns_retrieve_data_page() {
|
||||||
|
export _H1="Cookie:$1"
|
||||||
|
export _H2="Accept-Language:en-US"
|
||||||
|
data_id="$2"
|
||||||
|
url="https://freedns.afraid.org/subdomain/edit.php?data_id=$2"
|
||||||
|
|
||||||
|
_debug "Retrieve data page for ID $data_id from FreeDNS"
|
||||||
|
|
||||||
|
htmlpage="$(_get "$url")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "FreeDNS retrieve data page failed bad RC from _get"
|
||||||
|
return 1
|
||||||
|
elif [ -z "$htmlpage" ]; then
|
||||||
|
_err "FreeDNS returned empty data page"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
|
||||||
|
printf "%s" "$htmlpage"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_add_txt_record login_cookies domain_id subdomain value
|
||||||
|
# returns 0 success
|
||||||
|
_freedns_add_txt_record() {
|
||||||
|
export _H1="Cookie:$1"
|
||||||
|
export _H2="Accept-Language:en-US"
|
||||||
|
domain_id="$2"
|
||||||
|
subdomain="$3"
|
||||||
|
value="$(printf '%s' "$4" | _url_encode)"
|
||||||
|
url="https://freedns.afraid.org/subdomain/save.php?step=2"
|
||||||
|
|
||||||
|
htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "FreeDNS failed to add TXT record for $subdomain bad RC from _post"
|
||||||
|
return 1
|
||||||
|
elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
_err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
elif _contains "$htmlpage" "security code was incorrect"; then
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
_err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code"
|
||||||
|
_err "Note that you cannot use automatic DNS validation for FreeDNS public domains"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug3 "htmlpage: $htmlpage"
|
||||||
|
_info "Added acme challenge TXT record for $fulldomain at FreeDNS"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_delete_txt_record login_cookies data_id
|
||||||
|
# returns 0 success
|
||||||
|
_freedns_delete_txt_record() {
|
||||||
|
export _H1="Cookie:$1"
|
||||||
|
export _H2="Accept-Language:en-US"
|
||||||
|
data_id="$2"
|
||||||
|
url="https://freedns.afraid.org/subdomain/delete2.php"
|
||||||
|
|
||||||
|
htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "FreeDNS failed to delete TXT record for $data_id bad RC from _get"
|
||||||
|
return 1
|
||||||
|
elif ! _contains "$htmlheader" "200 OK"; then
|
||||||
|
_debug2 "htmlheader: $htmlheader"
|
||||||
|
_err "FreeDNS failed to delete TXT record $data_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deleted acme challenge TXT record for $fulldomain at FreeDNS"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_domain_id domain_name
|
||||||
|
# echo the domain_id if found
|
||||||
|
# return 0 success
|
||||||
|
_freedns_domain_id() {
|
||||||
|
# Start by escaping the dots in the domain name
|
||||||
|
search_domain="$(echo "$1" | sed 's/\./\\./g')"
|
||||||
|
|
||||||
|
# Sometimes FreeDNS does not return the subdomain page but rather
|
||||||
|
# returns a page regarding becoming a premium member. This usually
|
||||||
|
# happens after a period of inactivity. Immediately trying again
|
||||||
|
# returns the correct subdomain page. So, we will try twice to
|
||||||
|
# load the page and obtain our domain ID
|
||||||
|
attempts=2
|
||||||
|
while [ "$attempts" -gt "0" ]; do
|
||||||
|
attempts="$(_math "$attempts" - 1)"
|
||||||
|
|
||||||
|
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
if [ "$using_cached_cookies" = "true" ]; then
|
||||||
|
_err "Has your FreeDNS username and password changed? If so..."
|
||||||
|
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' |
|
||||||
|
grep "<td>$search_domain</td>\|<td>$search_domain(.*)</td>" |
|
||||||
|
sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' |
|
||||||
|
cut -d = -f 2)"
|
||||||
|
# The above beauty extracts domain ID from the html page...
|
||||||
|
# strip out all blank space and new lines. Then insert newlines
|
||||||
|
# before each table row <tr>
|
||||||
|
# search for the domain within each row (which may or may not have
|
||||||
|
# a text string in brackets (.*) after it.
|
||||||
|
# And finally extract the domain ID.
|
||||||
|
if [ -n "$domain_id" ]; then
|
||||||
|
printf "%s" "$domain_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)"
|
||||||
|
done
|
||||||
|
_debug "Domain $search_domain not found after retry"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage _freedns_data_id domain_name record_type
|
||||||
|
# echo the data_id(s) if found
|
||||||
|
# return 0 success
|
||||||
|
_freedns_data_id() {
|
||||||
|
# Start by escaping the dots in the domain name
|
||||||
|
search_domain="$(echo "$1" | sed 's/\./\\./g')"
|
||||||
|
record_type="$2"
|
||||||
|
|
||||||
|
# Sometimes FreeDNS does not return the subdomain page but rather
|
||||||
|
# returns a page regarding becoming a premium member. This usually
|
||||||
|
# happens after a period of inactivity. Immediately trying again
|
||||||
|
# returns the correct subdomain page. So, we will try twice to
|
||||||
|
# load the page and obtain our domain ID
|
||||||
|
attempts=2
|
||||||
|
while [ "$attempts" -gt "0" ]; do
|
||||||
|
attempts="$(_math "$attempts" - 1)"
|
||||||
|
|
||||||
|
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
if [ "$using_cached_cookies" = "true" ]; then
|
||||||
|
_err "Has your FreeDNS username and password changed? If so..."
|
||||||
|
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
data_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' |
|
||||||
|
grep "<td[a-zA-Z=#]*>$record_type</td>" |
|
||||||
|
grep "<ahref.*>$search_domain</a>" |
|
||||||
|
sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' |
|
||||||
|
cut -d = -f 2)"
|
||||||
|
# The above beauty extracts data ID from the html page...
|
||||||
|
# strip out all blank space and new lines. Then insert newlines
|
||||||
|
# before each table row <tr>
|
||||||
|
# search for the record type withing each row (e.g. TXT)
|
||||||
|
# search for the domain within each row (which is within a <a..>
|
||||||
|
# </a> anchor. And finally extract the domain ID.
|
||||||
|
if [ -n "$data_id" ]; then
|
||||||
|
printf "%s" "$data_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)"
|
||||||
|
done
|
||||||
|
_debug "Domain $search_domain not found after retry"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
175
dnsapi/dns_gandi_livedns.sh
Normal file
175
dnsapi/dns_gandi_livedns.sh
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Gandi LiveDNS v5 API
|
||||||
|
# http://doc.livedns.gandi.net/
|
||||||
|
# currently under beta
|
||||||
|
#
|
||||||
|
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
|
||||||
|
#
|
||||||
|
#Author: Frédéric Crozat <fcrozat@suse.com>
|
||||||
|
# Dominik Röttsches <drott@google.com>
|
||||||
|
#Report Bugs here: https://github.com/fcrozat/acme.sh
|
||||||
|
#
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
|
||||||
|
|
||||||
|
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_gandi_livedns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$GANDI_LIVEDNS_KEY" ]; then
|
||||||
|
_err "No API key specified for Gandi LiveDNS."
|
||||||
|
_err "Create your key and export it as GANDI_LIVEDNS_KEY"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
_debug sub_domain "$_sub_domain"
|
||||||
|
|
||||||
|
_dns_gandi_append_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_gandi_livedns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
_debug sub_domain "$_sub_domain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _dns_gandi_existing_rrset_values "$_domain" "$_sub_domain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_new_rrset_values=$(echo "$_rrset_values" | sed "s/...$txtvalue...//g")
|
||||||
|
# Cleanup dangling commata.
|
||||||
|
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, ,/ ,/g")
|
||||||
|
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, *\]/\]/g")
|
||||||
|
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g")
|
||||||
|
_debug "New rrset_values" "$_new_rrset_values"
|
||||||
|
|
||||||
|
_gandi_livedns_rest PUT \
|
||||||
|
"domains/$_domain/records/$_sub_domain/TXT" \
|
||||||
|
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" &&
|
||||||
|
_contains "$response" '{"message": "DNS Record Created"}' &&
|
||||||
|
_info "Removing record $(__green "success")"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _gandi_livedns_rest GET "domains/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" '"code": 401'; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
elif _contains "$response" '"code": 404'; then
|
||||||
|
_debug "$h not found"
|
||||||
|
else
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain="$h"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
p="$i"
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_gandi_append_record() {
|
||||||
|
domain=$1
|
||||||
|
sub_domain=$2
|
||||||
|
txtvalue=$3
|
||||||
|
|
||||||
|
if _dns_gandi_existing_rrset_values "$domain" "$sub_domain"; then
|
||||||
|
_debug "Appending new value"
|
||||||
|
_rrset_values=$(echo "$_rrset_values" | sed "s/\"]/\",\"$txtvalue\"]/")
|
||||||
|
else
|
||||||
|
_debug "Creating new record" "$_rrset_values"
|
||||||
|
_rrset_values="[\"$txtvalue\"]"
|
||||||
|
fi
|
||||||
|
_debug new_rrset_values "$_rrset_values"
|
||||||
|
_gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \
|
||||||
|
"{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" &&
|
||||||
|
_contains "$response" '{"message": "DNS Record Created"}' &&
|
||||||
|
_info "Adding record $(__green "success")"
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns_gandi_existing_rrset_values() {
|
||||||
|
domain=$1
|
||||||
|
sub_domain=$2
|
||||||
|
if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _contains "$response" '"rrset_type": "TXT"'; then
|
||||||
|
_debug "Does not have a _acme-challenge TXT record yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" '"rrset_values": \[\]'; then
|
||||||
|
_debug "Empty rrset_values for TXT record, no previous TXT record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "Already has TXT record."
|
||||||
|
_rrset_values=$(echo "$response" | _egrep_o 'rrset_values.*\[.*\]' |
|
||||||
|
_egrep_o '\[".*\"]')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_gandi_livedns_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
|
||||||
|
|
||||||
|
if [ "$m" = "GET" ]; then
|
||||||
|
response="$(_get "$GANDI_LIVEDNS_API/$ep")"
|
||||||
|
else
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
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